Here test the strength of recovery of edges of the networks for Connectnace

Here test the strength of recovery of edges of the networks on covariate inflated data passed to LIMON and passed to SPIEC-EASI. These will be compared to non-covariate inflated data passed to SPIEC-EASI. We utilize the LIMON SPIEC-EASI functions to iteratively generate the networks for covariate data passed to SPIEC-EASI, but bypass the linear mixed effect model distribution fitting step of LIMON. This is repeated 5 times for increase GLV connectance of 0.10, 0.20, 0.50, 0.75, and 0.9.

1. Load Library


Packages required to run the script

library(tidyverse)
── Attaching core tidyverse packages ──────────────────────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.1     ✔ tibble    3.2.1
✔ lubridate 1.9.4     ✔ tidyr     1.3.1
✔ purrr     1.0.4     ── Conflicts ────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
library(igraph)

Attaching package: ‘igraph’

The following objects are masked from ‘package:lubridate’:

    %--%, union

The following objects are masked from ‘package:dplyr’:

    as_data_frame, groups, union

The following objects are masked from ‘package:purrr’:

    compose, simplify

The following object is masked from ‘package:tidyr’:

    crossing

The following object is masked from ‘package:tibble’:

    as_data_frame

The following objects are masked from ‘package:stats’:

    decompose, spectrum

The following object is masked from ‘package:base’:

    union
library(NBZIMM)

Attaching package: ‘NBZIMM’

The following object is masked from ‘package:stringr’:

    fixed
library(SpiecEasi)

Attaching package: ‘SpiecEasi’

The following object is masked from ‘package:igraph’:

    make_graph
library(LIMON)
library(here)
here() starts at /Users/bealab/Desktop/Long_networks
library(lme4)
Loading required package: Matrix

Attaching package: ‘Matrix’

The following objects are masked from ‘package:SpiecEasi’:

    tril, triu

The following objects are masked from ‘package:tidyr’:

    expand, pack, unpack
library(Matrix)
library(tscount)
library(patchwork)
library(MASS)

Attaching package: ‘MASS’

The following object is masked from ‘package:patchwork’:

    area

The following object is masked from ‘package:SpiecEasi’:

    fitdistr

The following object is masked from ‘package:dplyr’:

    select
library(matrixcalc)

Attaching package: ‘matrixcalc’

The following object is masked from ‘package:igraph’:

    %s%
library(gridExtra)

Attaching package: ‘gridExtra’

The following object is masked from ‘package:dplyr’:

    combine
library(devtools)
Loading required package: usethis
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
library(miaSim)
Loading required package: TreeSummarizedExperiment
Loading required package: SingleCellExperiment
Loading required package: SummarizedExperiment
Loading required package: MatrixGenerics
Loading required package: matrixStats

Attaching package: ‘matrixStats’

The following object is masked from ‘package:dplyr’:

    count


Attaching package: ‘MatrixGenerics’

The following objects are masked from ‘package:matrixStats’:

    colAlls, colAnyNAs, colAnys, colAvgsPerRowSet, colCollapse, colCounts,
    colCummaxs, colCummins, colCumprods, colCumsums, colDiffs, colIQRDiffs, colIQRs,
    colLogSumExps, colMadDiffs, colMads, colMaxs, colMeans2, colMedians, colMins,
    colOrderStats, colProds, colQuantiles, colRanges, colRanks, colSdDiffs, colSds,
    colSums2, colTabulates, colVarDiffs, colVars, colWeightedMads, colWeightedMeans,
    colWeightedMedians, colWeightedSds, colWeightedVars, rowAlls, rowAnyNAs, rowAnys,
    rowAvgsPerColSet, rowCollapse, rowCounts, rowCummaxs, rowCummins, rowCumprods,
    rowCumsums, rowDiffs, rowIQRDiffs, rowIQRs, rowLogSumExps, rowMadDiffs, rowMads,
    rowMaxs, rowMeans2, rowMedians, rowMins, rowOrderStats, rowProds, rowQuantiles,
    rowRanges, rowRanks, rowSdDiffs, rowSds, rowSums2, rowTabulates, rowVarDiffs,
    rowVars, rowWeightedMads, rowWeightedMeans, rowWeightedMedians, rowWeightedSds,
    rowWeightedVars

Loading required package: GenomicRanges
Loading required package: stats4
Loading required package: BiocGenerics

Attaching package: ‘BiocGenerics’

The following object is masked from ‘package:gridExtra’:

    combine

The following objects are masked from ‘package:igraph’:

    normalize, path, union

The following objects are masked from ‘package:lubridate’:

    intersect, setdiff, union

The following objects are masked from ‘package:dplyr’:

    combine, intersect, setdiff, union

The following objects are masked from ‘package:stats’:

    IQR, mad, sd, var, xtabs

The following objects are masked from ‘package:base’:

    anyDuplicated, aperm, append, as.data.frame, basename, cbind, colnames, dirname,
    do.call, duplicated, eval, evalq, Filter, Find, get, grep, grepl, intersect,
    is.unsorted, lapply, Map, mapply, match, mget, order, paste, pmax, pmax.int,
    pmin, pmin.int, Position, rank, rbind, Reduce, rownames, sapply, setdiff, table,
    tapply, union, unique, unsplit, which.max, which.min

Loading required package: S4Vectors

Attaching package: ‘S4Vectors’

The following objects are masked from ‘package:Matrix’:

    expand, unname

The following objects are masked from ‘package:lubridate’:

    second, second<-

The following objects are masked from ‘package:dplyr’:

    first, rename

The following object is masked from ‘package:tidyr’:

    expand

The following object is masked from ‘package:utils’:

    findMatches

The following objects are masked from ‘package:base’:

    expand.grid, I, unname

Loading required package: IRanges

Attaching package: ‘IRanges’

The following object is masked from ‘package:lubridate’:

    %within%

The following objects are masked from ‘package:dplyr’:

    collapse, desc, slice

The following object is masked from ‘package:purrr’:

    reduce

Loading required package: GenomeInfoDb
Loading required package: Biobase
Welcome to Bioconductor

    Vignettes contain introductory material; view with 'browseVignettes()'. To cite
    Bioconductor, see 'citation("Biobase")', and for packages 'citation("pkgname")'.


Attaching package: ‘Biobase’

The following object is masked from ‘package:MatrixGenerics’:

    rowMedians

The following objects are masked from ‘package:matrixStats’:

    anyMissing, rowMedians

Loading required package: Biostrings
Loading required package: XVector

Attaching package: ‘XVector’

The following object is masked from ‘package:purrr’:

    compact


Attaching package: ‘Biostrings’

The following object is masked from ‘package:base’:

    strsplit
library(reshape2)

Attaching package: ‘reshape2’

The following object is masked from ‘package:tidyr’:

    smiths
library(ggpubr)
library(broom)
library(ggnewscale)
library(coin)
Loading required package: survival

1.2 - Functions

This function will be used to define which edges are consistent across different networks

edge_recovery <- function(data) {
  data <- data %>%
    mutate(
      Recovery = case_when(
        !is.na(L_Edge_weight) & !is.na(O_Edge_weight) & !is.na(Cov_Edge_weight) &
          (L_Edge_weight != 0 & O_Edge_weight != 0 & Cov_Edge_weight != 0) ~ 3,
        !is.na(O_Edge_weight) & !is.na(Cov_Edge_weight) &
          (O_Edge_weight != 0 & Cov_Edge_weight != 0) ~ 2,
        !is.na(L_Edge_weight) & !is.na(O_Edge_weight) &
          (L_Edge_weight != 0 & O_Edge_weight != 0) ~ 1,
        TRUE ~ 0
      )
    )
  
  data
}

2. Model Testing


2.1 - Connectance = 0.10

2.1.1 - LIMON on Covariate Inflated Data

Run the LIMON covariate correction and network inference steps on Covariate inflated data. These edges will be required as L_Edges

Make LIMON Object

SimData <- read.csv(here("Data", "GLV_SimData", "Dataset_4","GLV_Cov_0.10.csv"))

SimData <- column_to_rownames(SimData, "X")

# metadata
meta_data <- SimData[,1:5]
meta_data$Time <- as.numeric(meta_data$Time)
meta_data$BMI <- as.numeric(meta_data$BMI)
meta_data$Age <- as.numeric(meta_data$Age)

# Option to shorten to run faster
limon_meta <- meta_data #%>% filter(Time %in% c(1,2,3,4,5))

# Count data
limon_counts <- as.matrix(SimData[,6:55])

# sort rows
limon_counts <- limon_counts[rownames(limon_meta),]

  
L_obj <- LIMON_Obj(Counts = limon_counts, 
                           SampleData = limon_meta)

Distribution Fitting and check

# Set seed
set.seed(12345)
# Fit the distribution/remove covariates
#################################################################################
L_obj2 <- LIMON_DistrFit(Obj = L_obj, 
                           Time = "Time", 
                           Subject = "ID", 
                           Covariates = c("Sex", "BMI", "Age"),
                           model = "Sex",
                           distribution = "LMM")

Networks of LIMON Data

set.seed(12345)
pseed <- list(rep.num=50, seed=10010)
# SPIEC-EASI per time
L_obj3 <- LIMON_NetInf_Time(Obj = L_obj2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                            
  |                                                                                      |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=========                                                                             |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=================                                                                     |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==========================                                                            |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==================================                                                    |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |===========================================                                           |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |====================================================                                  |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |============================================================                          |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=====================================================================                 |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=============================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |======================================================================================| 100%
# Print Networks
L_obj4 <- LIMON_Edges_Networks(L_obj3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, 
                                       vertex.label.color = "black")

2.1.2 - True Networks - no Covariates

SPIEC-EASI Networks of the Raw Data without covariates. These will be labeled as O_Edges (for original edges). We read in the data and then assign it to a downstream LIMOn object to run SPIEC-EASI across all the timepoints but bypassing the network inference step

# Make fake LIMON Object to Pass to Network Inference Steps
##################################################################################
original_data <- read.csv(here("Data", "GLV_SimData", "Dataset_4","GLV_0.10.csv"))
original_data <- original_data %>% column_to_rownames("X")

# Loop through the time points, filter data, and remove metadata
counts_list <- list()
for (i in 1:10) {
  filtered_counts <- original_data %>% filter(Time == i)
  counts_list[[i]] <- round(filtered_counts[, 6:55])
}

# Initialize L_obj_sim2
L_obj_sim2 <- L_obj2

# Assign the processed data to the corresponding slots in L_obj_sim2
for (i in 1:10) {
  L_obj_sim2[["Corrected_Counts_Time"]][[paste0("Corrected_Counts_", i)]] <- counts_list[[i]]
}


# Network Inference
##################################################################################
# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)
# SPIEC-EASI per time
L_obj_sim3 <- LIMON_NetInf_Time(Obj = L_obj_sim2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                            
  |                                                                                      |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=========                                                                             |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=================                                                                     |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==========================                                                            |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==================================                                                    |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |===========================================                                           |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |====================================================                                  |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |============================================================                          |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=====================================================================                 |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=============================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |======================================================================================| 100%
# Print Networks
L_obj_sim4 <- LIMON_Edges_Networks(L_obj_sim3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, vertex.label.color = "black")

NA

2.1.3 - SPIEC-EASI on Covariate Data

Similar to process for original networks, except now we will run it on covariate conflated data. These will be labeled as Cov_Edges

# Make fake LIMON Object to Pass to Network Inference Steps
##################################################################################
original_data <- read.csv(here("Data", "GLV_SimData", "Dataset_4","GLV_Cov_0.10.csv"))
original_data <- original_data %>% column_to_rownames("X")

# Loop through the time points, filter data, and remove metadata
counts_list <- list()
for (i in 1:10) {
  filtered_counts <- original_data %>% filter(Time == i)
  counts_list[[i]] <- round(filtered_counts[, 6:55])
}

# Initialize L_obj_sim2
L_obj_sim2 <- L_obj2

# Assign the processed data to the corresponding slots in L_obj_sim2
for (i in 1:10) {
  L_obj_sim2[["Corrected_Counts_Time"]][[paste0("Corrected_Counts_", i)]] <- counts_list[[i]]
}



# Network Inference
##################################################################################
# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)
# SPIEC-EASI per time
L_obj_sim3 <- LIMON_NetInf_Time(Obj = L_obj_sim2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                            
  |                                                                                      |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=========                                                                             |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=================                                                                     |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==========================                                                            |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==================================                                                    |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |===========================================                                           |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |====================================================                                  |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |============================================================                          |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=====================================================================                 |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=============================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |======================================================================================| 100%
# Print Networks
L_obj_sim5 <- LIMON_Edges_Networks(L_obj_sim3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, vertex.label.color = "black")

NA
NA
NA

2.1.4 - Merge Outcomes

Calculate the similar edges across the three data types

# LIMON Data - Extract Edges
################################################################################

# Initialize an empty list to store the data frames
L_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj4[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # Rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("L_Edge_weight"="Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  L_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
L_Edges <- bind_rows(L_Edges_List)

# Original Data + Cov - Extract edges
################################################################################

# Initialize an empty list to store the data frames
Cov_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj_sim5[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # Rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("Cov_Edge_weight"="Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  Cov_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
Cov_Edges <- bind_rows(Cov_Edges_List)

# Original Data - Extract Edges
################################################################################
# Initialize an empty list to store the data frames
O_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj_sim4[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # Rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("O_Edge_weight"="Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  O_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
O_Edges <- bind_rows(O_Edges_List)

# Bind data together
################################################################################
Merged_T1 <- merge(L_Edges, O_Edges, by.y = c("Source", "Sink", "Time"), all=TRUE)
Merged_T1 <- merge(Merged_T1, Cov_Edges, by.y = c("Source", "Sink", "Time"), all=TRUE)


# Comparison Graph Data
################################################################################

# Identify true edges
Merged_T10 <- Merged_T1 %>%
  edge_recovery() 

# Add Connectance 
Merged_T10$Connectance <- 0.10

2.2 - Connectance = 0.20

2.2.1 - LIMON on Covariate Inflated Data

Run the LIMON covariate correction and network inference steps on Covariate inflated data. These edges will be required as L_Edges

Make LIMON Object

SimData <- read.csv(here("Data", "GLV_SimData", "Dataset_4","GLV_Cov_0.20.csv"))
SimData <- column_to_rownames(SimData, "X")

# metadata
meta_data <- SimData[,1:5]
meta_data$Time <- as.numeric(meta_data$Time)
meta_data$BMI <- as.numeric(meta_data$BMI)
meta_data$Age <- as.numeric(meta_data$Age)

# Option to shorten to run faster
limon_meta <- meta_data #%>% filter(Time %in% c(1,2,3,4,5))

# Count data
limon_counts <- as.matrix(SimData[,6:55])

# sort rows
limon_counts <- limon_counts[rownames(limon_meta),]

  
L_obj <- LIMON_Obj(Counts = limon_counts, 
                           SampleData = limon_meta)

Distribution Fitting and check

# Set seed
set.seed(12345)
# Fit the distribution/remove covariates
#################################################################################
L_obj2 <- LIMON_DistrFit(Obj = L_obj, 
                           Time = "Time", 
                           Subject = "ID", 
                           Covariates = c("Sex", "BMI", "Age"),
                           model = "Sex",
                           distribution = "LMM")

Networks of LIMON Data

# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)

# SPIEC-EASI per time
L_obj3 <- LIMON_NetInf_Time(Obj = L_obj2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                            
  |                                                                                      |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=========                                                                             |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=================                                                                     |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==========================                                                            |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==================================                                                    |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |===========================================                                           |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |====================================================                                  |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |============================================================                          |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=====================================================================                 |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=============================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |======================================================================================| 100%
# Print Networks
L_obj4 <- LIMON_Edges_Networks(L_obj3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, 
                                       vertex.label.color = "black")

2.2.2 - True Networks - no Covariates

SPIEC-EASI Networks of the Raw Data without covariates. These will be labeled as O_Edges (for original edges). We read in the data and then assign it to a downstream LIMOn object to run SPIEC-EASI across all the timepoints but bypassing the network inference step

# Make fake LIMON Object to Pass to Network Inference Steps
##################################################################################
original_data <- read.csv(here("Data", "GLV_SimData", "Dataset_4","GLV_0.20.csv"))
original_data <- original_data %>% column_to_rownames("X")

# Loop through the time points, filter data, and remove metadata
counts_list <- list()
for (i in 1:10) {
  filtered_counts <- original_data %>% filter(Time == i)
  counts_list[[i]] <- round(filtered_counts[, 6:55])
}

# Initialize L_obj_sim2
L_obj_sim2 <- L_obj2

# Assign the processed data to the corresponding slots in L_obj_sim2
for (i in 1:10) {
  L_obj_sim2[["Corrected_Counts_Time"]][[paste0("Corrected_Counts_", i)]] <- counts_list[[i]]
}


# Network Inference
##################################################################################
# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)
# SPIEC-EASI per time
L_obj_sim3 <- LIMON_NetInf_Time(Obj = L_obj_sim2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                            
  |                                                                                      |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=========                                                                             |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=================                                                                     |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==========================                                                            |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==================================                                                    |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |===========================================                                           |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |====================================================                                  |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |============================================================                          |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=====================================================================                 |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=============================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |======================================================================================| 100%
# Print Networks
L_obj_sim4 <- LIMON_Edges_Networks(L_obj_sim3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, vertex.label.color = "black")

NA
NA
NA

2.2.3 - SPIEC-EASI on Covariate Data

Similar to process for original networks, except now we will run it on covariate conflated data. These will be labeled as Cov_Edges

# Make fake LIMON Object to Pass to Network Inference Steps
##################################################################################
original_data <- read.csv(here("Data", "GLV_SimData", "Dataset_4","GLV_Cov_0.20.csv"))
original_data <- original_data %>% column_to_rownames("X")

# Loop through the time points, filter data, and remove metadata
counts_list <- list()
for (i in 1:10) {
  filtered_counts <- original_data %>% filter(Time == i)
  counts_list[[i]] <- round(filtered_counts[, 6:55])
}

# Initialize L_obj_sim2
L_obj_sim2 <- L_obj2

# Assign the processed data to the corresponding slots in L_obj_sim2
for (i in 1:10) {
  L_obj_sim2[["Corrected_Counts_Time"]][[paste0("Corrected_Counts_", i)]] <- counts_list[[i]]
}


# Network Inference
##################################################################################
# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)
# SPIEC-EASI per time
L_obj_sim3 <- LIMON_NetInf_Time(Obj = L_obj_sim2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                            
  |                                                                                      |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=========                                                                             |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=================                                                                     |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==========================                                                            |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==================================                                                    |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |===========================================                                           |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |====================================================                                  |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |============================================================                          |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=====================================================================                 |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=============================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |======================================================================================| 100%
# Print Networks
L_obj_sim5 <- LIMON_Edges_Networks(L_obj_sim3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, vertex.label.color = "black")

NA
NA
NA

2.2.4 - Merge Outcomes

Calculate the similar edges across the three data types

# LIMON Data - Extract Edges
################################################################################

# Initialize an empty list to store the data frames
L_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj4[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # Rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("L_Edge_weight" = "Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  L_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
L_Edges <- bind_rows(L_Edges_List)


# Original Data + Cov - Extract edges
################################################################################

# Initialize an empty list to store the data frames
Cov_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj_sim5[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # Rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("Cov_Edge_weight"= "Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  Cov_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
Cov_Edges <- bind_rows(Cov_Edges_List)

# Original Data - Extract Edges
################################################################################
# Initialize an empty list to store the data frames
O_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj_sim4[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # Rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("O_Edge_weight"= "Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  O_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
O_Edges <- bind_rows(O_Edges_List)


# Bind data together
################################################################################
Merged_T1 <- merge(L_Edges, O_Edges, by.y = c("Source", "Sink", "Time"), all=TRUE)
Merged_T1 <- merge(Merged_T1, Cov_Edges, by.y = c("Source", "Sink", "Time"), all=TRUE)


# Comparison Graph Data
################################################################################

# Identify true edges
Merged_T20 <- Merged_T1 %>%
  edge_recovery() 

# Add Connectance 
Merged_T20$Connectance <- 0.20

2.3 - Connectance = 0.50

2.3.1 - LIMON on Covariate Inflated Data

Run the LIMON covariate correction and network inference steps on Covariate inflated data. These edges will be required as L_Edges

Make LIMON Object

SimData <- read.csv(here("Data", "GLV_SimData", "Dataset_4","GLV_Cov_0.50.csv"))
SimData <- column_to_rownames(SimData, "X")

# metadata
meta_data <- SimData[,1:5]
meta_data$Time <- as.numeric(meta_data$Time)
meta_data$BMI <- as.numeric(meta_data$BMI)
meta_data$Age <- as.numeric(meta_data$Age)

# Option to shorten to run faster
limon_meta <- meta_data #%>% filter(Time %in% c(1,2,3,4,5))

# Count data
limon_counts <- as.matrix(SimData[,6:55])

# sort rows
limon_counts <- limon_counts[rownames(limon_meta),]

  
L_obj <- LIMON_Obj(Counts = limon_counts, 
                           SampleData = limon_meta)

Distribution Fitting and check

# Set seed
set.seed(12345)
# Fit the distribution/remove covariates
#################################################################################
L_obj2 <- LIMON_DistrFit(Obj = L_obj, 
                           Time = "Time", 
                           Subject = "ID", 
                           Covariates = c("Sex", "BMI", "Age"),
                           model = "Sex",
                           distribution = "LMM")

Networks of LIMON Data

# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)

# SPIEC-EASI per time
L_obj3 <- LIMON_NetInf_Time(Obj = L_obj2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                            
  |                                                                                      |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=========                                                                             |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=================                                                                     |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==========================                                                            |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==================================                                                    |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |===========================================                                           |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |====================================================                                  |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |============================================================                          |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=====================================================================                 |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=============================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |======================================================================================| 100%
# Print Networks
L_obj4 <- LIMON_Edges_Networks(L_obj3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, 
                                       vertex.label.color = "black")

2.3.2 - True Networks - no Covariates

SPIEC-EASI Networks of the Raw Data without covariates. These will be labeled as O_Edges (for original edges). We read in the data and then assign it to a downstream LIMOn object to run SPIEC-EASI across all the time points but bypassing the network inference step

# Make fake LIMON Object to Pass to Network Inference Steps
##################################################################################
original_data <- read.csv(here("Data", "GLV_SimData", "Dataset_4","GLV_0.50.csv"))
original_data <- original_data %>% column_to_rownames("X")

# Loop through the time points, filter data, and remove metadata
counts_list <- list()
for (i in 1:10) {
  filtered_counts <- original_data %>% filter(Time == i)
  counts_list[[i]] <- round(filtered_counts[, 6:55])
}

# Initialize L_obj_sim2
L_obj_sim2 <- L_obj2

# Assign the processed data to the corresponding slots in L_obj_sim2
for (i in 1:10) {
  L_obj_sim2[["Corrected_Counts_Time"]][[paste0("Corrected_Counts_", i)]] <- counts_list[[i]]
}


# Network Inference
##################################################################################
# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)
# SPIEC-EASI per time
L_obj_sim3 <- LIMON_NetInf_Time(Obj = L_obj_sim2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                            
  |                                                                                      |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=========                                                                             |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=================                                                                     |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==========================                                                            |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==================================                                                    |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |===========================================                                           |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |====================================================                                  |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |============================================================                          |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=====================================================================                 |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=============================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |======================================================================================| 100%
# Print Networks
L_obj_sim4 <- LIMON_Edges_Networks(L_obj_sim3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, vertex.label.color = "black")

NA
NA
NA

2.3.3 - SPIEC-EASI on Covariate Data

Similar to process for original networks, except now we will run it on covariate conflated data. These will be labeled as Cov_Edges

# Make fake LIMON Object to Pass to Network Inference Steps
##################################################################################
original_data <- read.csv(here("Data", "GLV_SimData", "Dataset_4","GLV_Cov_0.50.csv"))
original_data <- original_data %>% column_to_rownames("X")

# Loop through the time points, filter data, and remove metadata
counts_list <- list()
for (i in 1:10) {
  filtered_counts <- original_data %>% filter(Time == i)
  counts_list[[i]] <- round(filtered_counts[, 6:55])
}

# Initialize L_obj_sim2
L_obj_sim2 <- L_obj2

# Assign the processed data to the corresponding slots in L_obj_sim2
for (i in 1:10) {
  L_obj_sim2[["Corrected_Counts_Time"]][[paste0("Corrected_Counts_", i)]] <- counts_list[[i]]
}


# Network Inference
##################################################################################
# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)
# SPIEC-EASI per time
L_obj_sim3 <- LIMON_NetInf_Time(Obj = L_obj_sim2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                            
  |                                                                                      |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=========                                                                             |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=================                                                                     |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==========================                                                            |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==================================                                                    |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |===========================================                                           |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |====================================================                                  |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |============================================================                          |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=====================================================================                 |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=============================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |======================================================================================| 100%
# Print Networks
L_obj_sim5 <- LIMON_Edges_Networks(L_obj_sim3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, vertex.label.color = "black")

NA
NA
NA

2.3.4 - Merge Outcomes

Calculate the similar edges across the three data types

# LIMON Data - Extract Edges
################################################################################

# Initialize an empty list to store the data frames
L_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj4[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # Rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("L_Edge_weight"= "Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  L_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
L_Edges <- bind_rows(L_Edges_List)


# Original Data + Cov - Extract edges
################################################################################

# Initialize an empty list to store the data frames
Cov_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj_sim5[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # Rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("Cov_Edge_weight"= "Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  Cov_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
Cov_Edges <- bind_rows(Cov_Edges_List)

# Original Data - Extract Edges
################################################################################
# Initialize an empty list to store the data frames
O_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj_sim4[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # Rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("O_Edge_weight"= "Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  O_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
O_Edges <- bind_rows(O_Edges_List)

# Bind data together
################################################################################
Merged_T1 <- merge(L_Edges, O_Edges, by.y = c("Source", "Sink", "Time"), all=TRUE)
Merged_T1 <- merge(Merged_T1, Cov_Edges, by.y = c("Source", "Sink", "Time"), all=TRUE)


# Comparison Graph Data
################################################################################

# Identify true edges
Merged_T50 <- Merged_T1 %>%
  edge_recovery() 

# Add Connectance 
Merged_T50$Connectance <- 0.50

2.4 - Connectance = 0.75

2.4.1 - LIMON on Covariate Inflated Data

Run the LIMON covariate correction and network inference steps on Covariate inflated data. These edges will be required as L_Edges

Make LIMON Object

SimData <- read.csv(here("Data", "GLV_SimData", "Dataset_4","GLV_Cov_0.75.csv"))
SimData <- column_to_rownames(SimData, "X")

# metadata
meta_data <- SimData[,1:5]
meta_data$Time <- as.numeric(meta_data$Time)
meta_data$BMI <- as.numeric(meta_data$BMI)
meta_data$Age <- as.numeric(meta_data$Age)

# Option to shorten to run faster
limon_meta <- meta_data #%>% filter(Time %in% c(1,2,3,4,5))

# Count data
limon_counts <- as.matrix(SimData[,6:55])

# sort rows
limon_counts <- limon_counts[rownames(limon_meta),]

  
L_obj <- LIMON_Obj(Counts = limon_counts, 
                           SampleData = limon_meta)

Distribution Fitting and check

# Set seed
set.seed(12345)
# Fit the distribution/remove covariates
#################################################################################
L_obj2 <- LIMON_DistrFit(Obj = L_obj, 
                           Time = "Time", 
                           Subject = "ID", 
                           Covariates = c("Sex", "BMI", "Age"),
                           model = "Sex",
                           distribution = "LMM")

Networks of LIMON Data

# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)

# SPIEC-EASI per time
L_obj3 <- LIMON_NetInf_Time(Obj = L_obj2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                            
  |                                                                                      |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=========                                                                             |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=================                                                                     |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==========================                                                            |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==================================                                                    |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |===========================================                                           |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |====================================================                                  |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |============================================================                          |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=====================================================================                 |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=============================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |======================================================================================| 100%
# Print Networks
L_obj4 <- LIMON_Edges_Networks(L_obj3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, 
                                       vertex.label.color = "black")

2.4.2 - True Networks - no Covariates

SPIEC-EASI Networks of the Raw Data without covariates. These will be labeled as O_Edges (for original edges). We read in the data and then assign it to a downstream LIMOn object to run SPIEC-EASI across all the timepoints but bypassing the network inference step

# Make fake LIMON Object to Pass to Network Inference Steps
##################################################################################
original_data <- read.csv(here("Data", "GLV_SimData", "Dataset_4","GLV_0.75.csv"))
original_data <- original_data %>% column_to_rownames("X")

# Loop through the time points, filter data, and remove metadata
counts_list <- list()
for (i in 1:10) {
  filtered_counts <- original_data %>% filter(Time == i)
  counts_list[[i]] <- round(filtered_counts[, 6:55])
}

# Initialize L_obj_sim2
L_obj_sim2 <- L_obj2

# Assign the processed data to the corresponding slots in L_obj_sim2
for (i in 1:10) {
  L_obj_sim2[["Corrected_Counts_Time"]][[paste0("Corrected_Counts_", i)]] <- counts_list[[i]]
}


# Network Inference
##################################################################################
# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)
# SPIEC-EASI per time
L_obj_sim3 <- LIMON_NetInf_Time(Obj = L_obj_sim2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                            
  |                                                                                      |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=========                                                                             |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=================                                                                     |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==========================                                                            |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==================================                                                    |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |===========================================                                           |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |====================================================                                  |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |============================================================                          |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=====================================================================                 |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=============================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |======================================================================================| 100%
# Print Networks
L_obj_sim4 <- LIMON_Edges_Networks(L_obj_sim3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, vertex.label.color = "black")

NA
NA
NA

2.4.3 - SPIEC-EASI on Covariate Data

Similar to process for original networks, except now we will run it on covariate conflated data. These will be labeled as Cov_Edges

# Make fake LIMON Object to Pass to Network Inference Steps
##################################################################################
original_data <- read.csv(here("Data", "GLV_SimData", "Dataset_4","GLV_Cov_0.75.csv"))
original_data <- original_data %>% column_to_rownames("X")

# Loop through the time points, filter data, and remove metadata
counts_list <- list()
for (i in 1:10) {
  filtered_counts <- original_data %>% filter(Time == i)
  counts_list[[i]] <- round(filtered_counts[, 6:55])
}

# Initialize L_obj_sim2
L_obj_sim2 <- L_obj2

# Assign the processed data to the corresponding slots in L_obj_sim2
for (i in 1:10) {
  L_obj_sim2[["Corrected_Counts_Time"]][[paste0("Corrected_Counts_", i)]] <- counts_list[[i]]
}


# Network Inference
##################################################################################
# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)
# SPIEC-EASI per time
L_obj_sim3 <- LIMON_NetInf_Time(Obj = L_obj_sim2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                            
  |                                                                                      |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=========                                                                             |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=================                                                                     |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==========================                                                            |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==================================                                                    |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |===========================================                                           |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |====================================================                                  |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |============================================================                          |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=====================================================================                 |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=============================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |======================================================================================| 100%
# Print Networks
L_obj_sim5 <- LIMON_Edges_Networks(L_obj_sim3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, vertex.label.color = "black")

NA
NA
NA

2.4.4 - Merge Outcomes

Calculate the similar edges across the three data types

# LIMON Data - Extract Edges
################################################################################

# Initialize an empty list to store the data frames
L_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj4[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # Rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("L_Edge_weight"= "Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  L_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
L_Edges <- bind_rows(L_Edges_List)

# Original Data + Cov - Extract edges
################################################################################

# Initialize an empty list to store the data frames
Cov_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj_sim5[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # Rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("Cov_Edge_weight"= "Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  Cov_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
Cov_Edges <- bind_rows(Cov_Edges_List)

# Original Data - Extract Edges
################################################################################
# Initialize an empty list to store the data frames
O_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj_sim4[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # Rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("O_Edge_weight"= "Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  O_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
O_Edges <- bind_rows(O_Edges_List)

# Bind data together
################################################################################
Merged_T1 <- merge(L_Edges, O_Edges, by.y = c("Source", "Sink", "Time"), all=TRUE)
Merged_T1 <- merge(Merged_T1, Cov_Edges, by.y = c("Source", "Sink", "Time"), all=TRUE)


# Comparison Graph Data
################################################################################

# Identify true edges
Merged_T75 <- Merged_T1 %>%
  edge_recovery() 

# Add Connectance 
Merged_T75$Connectance <- 0.75

2.5 - Connectance = 0.90

2.5.1 - LIMON on Covariate Inflated Data

Run the LIMON covariate correction and network inference steps on Covariate inflated data. These edges will be required as L_Edges

Make LIMON Object

SimData <- read.csv(here("Data", "GLV_SimData", "Dataset_4","GLV_Cov_0.90.csv"))
SimData <- column_to_rownames(SimData, "X")

# metadata
meta_data <- SimData[,1:5]
meta_data$Time <- as.numeric(meta_data$Time)
meta_data$BMI <- as.numeric(meta_data$BMI)
meta_data$Age <- as.numeric(meta_data$Age)

# Option to shorten to run faster
limon_meta <- meta_data #%>% filter(Time %in% c(1,2,3,4,5))

# Count data
limon_counts <- as.matrix(SimData[,6:55])

# sort rows
limon_counts <- limon_counts[rownames(limon_meta),]

  
L_obj <- LIMON_Obj(Counts = limon_counts, 
                           SampleData = limon_meta)

Distribution Fitting and check

# Set seed
set.seed(12345)
# Fit the distribution/remove covariates
#################################################################################
L_obj2 <- LIMON_DistrFit(Obj = L_obj, 
                           Time = "Time", 
                           Subject = "ID", 
                           Covariates = c("Sex", "BMI", "Age"),
                           model = "Sex",
                           distribution = "LMM")

Networks of LIMON Data

# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)

# SPIEC-EASI per time
L_obj3 <- LIMON_NetInf_Time(Obj = L_obj2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                            
  |                                                                                      |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=========                                                                             |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=================                                                                     |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==========================                                                            |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==================================                                                    |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |===========================================                                           |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |====================================================                                  |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |============================================================                          |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=====================================================================                 |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=============================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |======================================================================================| 100%
# Print Networks
L_obj4 <- LIMON_Edges_Networks(L_obj3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, 
                                       vertex.label.color = "black")

2.5.2 - True Networks - no Covariates

SPIEC-EASI Networks of the Raw Data without covariates. These will be labeled as O_Edges (for original edges). We read in the data and then assign it to a downstream LIMOn object to run SPIEC-EASI across all the timepoints but bypassing the network inference step

# Make fake LIMON Object to Pass to Network Inference Steps
##################################################################################
original_data <- read.csv(here("Data", "GLV_SimData", "Dataset_4","GLV_0.90.csv"))
original_data <- original_data %>% column_to_rownames("X")

# Loop through the time points, filter data, and remove metadata
counts_list <- list()
for (i in 1:10) {
  filtered_counts <- original_data %>% filter(Time == i)
  counts_list[[i]] <- round(filtered_counts[, 6:55])
}

# Initialize L_obj_sim2
L_obj_sim2 <- L_obj2

# Assign the processed data to the corresponding slots in L_obj_sim2
for (i in 1:10) {
  L_obj_sim2[["Corrected_Counts_Time"]][[paste0("Corrected_Counts_", i)]] <- counts_list[[i]]
}


# Network Inference
##################################################################################
# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)
# SPIEC-EASI per time
L_obj_sim3 <- LIMON_NetInf_Time(Obj = L_obj_sim2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                            
  |                                                                                      |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=========                                                                             |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=================                                                                     |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==========================                                                            |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==================================                                                    |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |===========================================                                           |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |====================================================                                  |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |============================================================                          |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=====================================================================                 |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=============================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |======================================================================================| 100%
# Print Networks
L_obj_sim4 <- LIMON_Edges_Networks(L_obj_sim3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, vertex.label.color = "black")

NA
NA
NA

2.5.3 - SPIEC-EASI on Covariate Data

Similar to process for original networks, except now we will run it on covariate conflated data. These will be labeled as Cov_Edges

# Make fake LIMON Object to Pass to Network Inference Steps
##################################################################################
original_data <- read.csv(here("Data", "GLV_SimData", "Dataset_4","GLV_Cov_0.90.csv"))
original_data <- original_data %>% column_to_rownames("X")

# Loop through the time points, filter data, and remove metadata
counts_list <- list()
for (i in 1:10) {
  filtered_counts <- original_data %>% filter(Time == i)
  counts_list[[i]] <- round(filtered_counts[, 6:55])
}

# Initialize L_obj_sim2
L_obj_sim2 <- L_obj2

# Assign the processed data to the corresponding slots in L_obj_sim2
for (i in 1:10) {
  L_obj_sim2[["Corrected_Counts_Time"]][[paste0("Corrected_Counts_", i)]] <- counts_list[[i]]
}


# Network Inference
##################################################################################
# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)
# SPIEC-EASI per time
L_obj_sim3 <- LIMON_NetInf_Time(Obj = L_obj_sim2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                            
  |                                                                                      |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=========                                                                             |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=================                                                                     |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==========================                                                            |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |==================================                                                    |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |===========================================                                           |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |====================================================                                  |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |============================================================                          |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=====================================================================                 |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |=============================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                            
  |======================================================================================| 100%
# Print Networks
L_obj_sim5 <- LIMON_Edges_Networks(L_obj_sim3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, vertex.label.color = "black")

NA
NA
NA

2.5.4 - Merge Outcomes

Calculate the similar edges across the three data types

# LIMON Data - Extract Edges
################################################################################

# Initialize an empty list to store the data frames
L_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj4[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # Rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("L_Edge_weight"= "Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  L_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
L_Edges <- bind_rows(L_Edges_List)

# Original Data + Cov - Extract edges
################################################################################

# Initialize an empty list to store the data frames
Cov_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj_sim5[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # Rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("Cov_Edge_weight"= "Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  Cov_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
Cov_Edges <- bind_rows(Cov_Edges_List)

# Original Data - Extract Edges
################################################################################
# Initialize an empty list to store the data frames
O_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj_sim4[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # Rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("O_Edge_weight"= "Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  O_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
O_Edges <- bind_rows(O_Edges_List)

# Bind data together
################################################################################
Merged_T1 <- merge(L_Edges, O_Edges, by.y = c("Source", "Sink", "Time"), all=TRUE)
Merged_T1 <- merge(Merged_T1, Cov_Edges, by.y = c("Source", "Sink", "Time"), all=TRUE)


# Comparison Graph Data
################################################################################

# Identify true edges
Merged_T90 <- Merged_T1 %>%
  edge_recovery() 

# Add Connectance 
Merged_T90$Connectance <- 0.90

3 - Summary of Sensitivty Analysis


Merge data for the following graphical analysis

# Drop Source and Sink column and combine together
#################################################################
Merged_T1b <- Merged_T10 %>% dplyr::select(-Source, -Sink)
Merged_T20b <- Merged_T20 %>% dplyr::select(-Source, -Sink)
Merged_T50b <- Merged_T50 %>% dplyr::select(-Source, -Sink)
Merged_T75b <- Merged_T75 %>% dplyr::select(-Source, -Sink)
Merged_T90b <- Merged_T90 %>% dplyr::select(-Source, -Sink)

# Merge together
#################################################################
# Data for Graphs 1 and 2
Subject_sens_data_full <- rbind(Merged_T1b, Merged_T20b, Merged_T50b, Merged_T75b, Merged_T90b)

# Data for Graph 3
#Merged_T90c <- Merged_T90 %>% dplyr::select(-Edge_weight)
Merged_T10c <- Merged_T10[,c("Source","Sink","Time","L_Edge_weight",
                            "O_Edge_weight","Cov_Edge_weight","Recovery","Connectance")]
Subject_sens_data_taxa <- rbind(Merged_T10c, Merged_T20, Merged_T50, Merged_T75, Merged_T90)

Option to write and read in the above files

##write.csv(Subject_sens_data_full, here("Output", "Dataset_4", "Subject_sens_data_full.csv"))
#write.csv(Subject_sens_data_taxa, here("Output", "Dataset_4","Subject_sens_data_taxa.csv"))

Subject_sens_data_full <- read.csv(here("Output", "Dataset_4", "Subject_sens_data_full.csv"))
Subject_sens_data_taxa <- read.csv(here("Output","Dataset_4", "Subject_sens_data_taxa.csv"))

3.1 Graph Percent True Edges

Find the Percent True Edges Recovered per sample size per time point

3.1.1 - Data Formatting

# Loop through all timepoints
##########################################################
all_timepoints <- list()

for (time in 1:10) {
  # Filter data for the current Sample Size
  Subject_sens_data <- Subject_sens_data_full %>% filter(Time == time)
  
  # Countthe total True_Edges for that Sample Size
  ##########################################################
  
  Subject_sens_data2 <- Subject_sens_data %>%
    group_by(Connectance) %>%
    summarise(True_Edges = sum(O_Edge_weight != 0, na.rm = TRUE)) %>%
    ungroup()
  
  # Merge the summarized data back with the original data
  Subject_sens_data <- merge(Subject_sens_data, Subject_sens_data2, by="Connectance", all=TRUE)
  
  # Find the Percent True Edges Data
  ##########################################################
  
  # count the number of edges
  Percent_true <- Subject_sens_data  %>%
    group_by(Connectance, Recovery, True_Edges) %>%
    summarise(Count = n()) %>%
    ungroup() %>%
    filter(Recovery != 0)
  
  # Add the number of edges in all three data types to the LIMON vs Raw Speic-Easi counts
  Percent_true <- Percent_true %>%
    group_by(Connectance, True_Edges) %>%
    mutate(
      Count_3 = ifelse(Recovery == 3, Count, 0),
      Count = ifelse(Recovery == 1 | Recovery == 2, Count + sum(Count_3), Count)
    ) %>%
    dplyr::select(-Count_3)
  
  # Create a dataframe with all combinations of Connectance and Recovery
  combinations <- expand.grid(Connectance = unique(Percent_true$Connectance), Recovery = c(1, 2, 3))
  Percent_true <- merge(Percent_true, combinations, by=c("Connectance", "Recovery"), all=TRUE)
  
  # Fill and replace missing values
  Percent_true <- Percent_true %>%
    group_by(Connectance) %>%
    fill(True_Edges, .direction = "downup") %>%
    mutate(
      Count = ifelse(is.na(Count), Count[Recovery == 3][1], ifelse(is.na(Count), 0, Count))
    ) %>%
    ungroup() %>%
    replace_na(list(Count = 0)) %>% 
    mutate(DataType = case_when(
      Recovery == 1 ~ "LIMON",
      Recovery == 2 ~ "SPIEC-EASI",
      Recovery == 3 ~ "Both"
    ))
  
  # Calculate the Percentage of edges
  Percent_true$Percent_true <- Percent_true$Count / Percent_true$True_Edges
  Percent_true$Time <- time
  
  # Store the result in the list
  all_timepoints[[time]] <- Percent_true
}
`summarise()` has grouped output by 'Connectance', 'Recovery'. You can override using the `.groups` argument.`summarise()` has grouped output by 'Connectance', 'Recovery'. You can override using the `.groups` argument.`summarise()` has grouped output by 'Connectance', 'Recovery'. You can override using the `.groups` argument.`summarise()` has grouped output by 'Connectance', 'Recovery'. You can override using the `.groups` argument.`summarise()` has grouped output by 'Connectance', 'Recovery'. You can override using the `.groups` argument.`summarise()` has grouped output by 'Connectance', 'Recovery'. You can override using the `.groups` argument.`summarise()` has grouped output by 'Connectance', 'Recovery'. You can override using the `.groups` argument.`summarise()` has grouped output by 'Connectance', 'Recovery'. You can override using the `.groups` argument.`summarise()` has grouped output by 'Connectance', 'Recovery'. You can override using the `.groups` argument.`summarise()` has grouped output by 'Connectance', 'Recovery'. You can override using the `.groups` argument.
# Combine all timepoints together
Percent_true <- do.call(rbind, all_timepoints)

# Filter the combined data
Percent_true <- Percent_true %>% filter(DataType != "Both")

3.1.2 - Statistics

Check normalcy to determine if t-test or if wilcox rank sum

# Step 1 - Run Shapiro Wilks to test for normalacy of the data
#############################################################################
shapiro_result <- shapiro.test(Percent_true$Percent_true)
p_value <- shapiro_result$p.value

# Step 2 - Print p-value on the histogram
#############################################################################
# Create the histogram plot
hist(Percent_true$Percent_true, breaks=100, 
     main = "Distribution of Percent True", 
     xlab = "Percent True", ylab = "Frequency")
# Add the p-value annotation
text(x = 0.4, y = 10, 
     labels = paste("Shapiro-Wilk p-value: ", p_value))

Data is not normally distributed so use wilcox rank sum test instead of t-test

p-value label function

# P-value labels
p_value_sig <- function(p) {
  if (is.na(p)) {
    return(" ")
  } else if (p < 0.001) {
    return("***")
  } else if (p < 0.01) {
    return("**")
  } else if (p < 0.05) {
    return("*")
  } else {
    return(" ")
  }
}

Plot the findings


# Step 1: Summary Statistics
#################################################################

# Perform a T-tests and make summary statistics for plotting
Summary_data <- Percent_true %>%
  group_by(Connectance) %>%
  do({
    data = .
    
    # Check if the data for any group is constant
    group_variances <- data %>%
      group_by(DataType) %>%
      summarise(var = var(Percent_true, na.rm = TRUE))  # Handle NAs in variance calculation
    
    if (any(is.na(group_variances$var)) || any(group_variances$var == 0)) {
      # If data is constant or variance calculation resulted in NA, assign p-value as NA
      p_value <- NA
    } else {
      # Perform t-test
      wilcox <- exactRankTests::wilcox.exact(Percent_true ~ DataType, data = data)
      p_value <- tidy(wilcox)$p.value
    }
    
    summarise(data,
      mean_percent_true_LIMON = mean(Percent_true[DataType == "LIMON"], na.rm = TRUE),
      sd_percent_true_LIMON = sd(Percent_true[DataType == "LIMON"], na.rm = TRUE),
      n_LIMON = sum(DataType == "LIMON", na.rm = TRUE),
      mean_percent_true_SPIECEASI = mean(Percent_true[DataType == "SPIEC-EASI"], na.rm = TRUE),
      sd_percent_true_SPIECEASI = sd(Percent_true[DataType == "SPIEC-EASI"], na.rm = TRUE),
      n_SPIECEASI = sum(DataType == "SPIEC-EASI", na.rm = TRUE),
      p.value = p_value
    )
  }) %>%
  mutate(
    se_percent_true_LIMON = sd_percent_true_LIMON / sqrt(n_LIMON),
    se_percent_true_SPIECEASI = sd_percent_true_SPIECEASI / sqrt(n_SPIECEASI),
    significance = sapply(p.value, p_value_sig)
  )

# Step 2: Make the plotting data
#################################################################
Summary_data_long <- Summary_data %>%
  dplyr::select(Connectance, starts_with("mean_percent_true"), starts_with("se_percent_true"), significance) %>%
  pivot_longer(cols = starts_with("mean_percent_true"), names_to = "DataType", values_to = "mean_percent_true") %>%
  mutate(DataType = ifelse(str_detect(DataType, "LIMON"), "LIMON", "SPIEC-EASI")) %>%
  pivot_longer(cols = starts_with("se_percent_true"), names_to = "DataType_se", values_to = "se_percent_true") %>%
  mutate(DataType_se = ifelse(str_detect(DataType_se, "LIMON"), "LIMON", "SPIEC-EASI")) %>%
  filter(DataType == DataType_se) %>%
  dplyr::select(-DataType_se)

# Step 3: Final figure
#################################################################
ggplot(Summary_data_long, aes(x = Connectance, y = mean_percent_true, color = DataType)) +
  geom_line() +
  geom_point() +
  geom_errorbar(aes(ymin = mean_percent_true - se_percent_true, ymax = mean_percent_true + se_percent_true), width = 0.01) +
  geom_text(aes(label = significance, y = 0.9), size = 5, vjust = -0.5, check_overlap = TRUE, color = "black") + # p-value annotations
  labs(x = "Connectance", y = "Percent_true", color = "DataType") +
  ylim(0, 1) + 
  xlab("Connectance") +
  ylab("Percent Recovered Edges") +
  scale_color_manual(name = "Network", values = c("LIMON" = "orange", "SPIEC-EASI" = "blue")) +
  scale_x_continuous(limits = c(0.10, 1.00), breaks = c(0.10,0.20,0.50,0.75,0.90))  +
  theme_classic() +
  theme(axis.text.x = element_text(family = "arial",color = "black", size = 18),
        axis.text.y = element_text(family = "arial",color = "black", size = 18),
        axis.title.x = element_text(family = "arial",color = "black", size = 14),
        axis.title.y = element_text(family = "arial",color = "black", size = 14))

3.2 Graph Total Edges

3.2.1 - Data Formatting

Make Data frame for Total Edges per data Type by Time point

# Initialize an empty list to store results
total_edges_list <- list()

# Loop through each timepoint from 1 to 10
for (time in 1:10) {
  # Calculate Total Edges for the current timepoint
  total_edges <- Subject_sens_data_full %>%
    filter(Time == time) %>%
    group_by(Connectance) %>%
    summarize(
      Count_L_Edge_weight = sum(!is.na(L_Edge_weight)),
      Count_O_Edge_weight = sum(!is.na(O_Edge_weight)),
      Count_Cov_Edge_weight = sum(!is.na(Cov_Edge_weight))
    ) %>%
    pivot_longer(cols = starts_with("Count"), names_to = "Counts_Column_Name", values_to = "Count") %>%
    mutate(DataType = case_when(
      Counts_Column_Name == "Count_L_Edge_weight" ~ "LIMON",
      Counts_Column_Name == "Count_O_Edge_weight" ~ "True",
      Counts_Column_Name == "Count_Cov_Edge_weight" ~ "SPIEC-EASI"
    ))
  total_edges$Time <- time
  
  # Store the result in the list
  total_edges_list[[time]] <- total_edges
}

# Combine all timepoints together
Total_Edges <- do.call(rbind, total_edges_list)

3.2.2 - Statistics

Check normalcy to determine if t-test or if wilcox rank sum

# Step 1 - Run Shapiro Wilks to test for normalacy of the data
#############################################################################
shapiro_result <- shapiro.test(Total_Edges$Count)
p_value <- shapiro_result$p.value

# Step 2 - Print p-value on the histogram
#############################################################################
# Create the histogram plot
hist(Total_Edges$Count, breaks=100, 
     main = "Total Edges", 
     xlab = "Count of edges", ylab = "Frequency")
# Add the p-value annotation
text(x = 100, y = 10, 
     labels = paste("Shapiro-Wilk p-value: ", p_value))

Data is not normally distributed, use Wilcox Rank sum isntead of T-test

# Step 1: Summary Statistics
#################################################################
Summary_data <- Total_Edges %>%
  group_by(Connectance) %>%
  summarise(
    # LIMON
    mean_edges_LIMON = mean(Count[DataType == "LIMON"], na.rm = TRUE),
    sd_edges_LIMON = sd(Count[DataType == "LIMON"], na.rm = TRUE),
    n_LIMON = sum(DataType == "LIMON", na.rm = TRUE),
    
    # SPIEC-EASI + COV
    mean_edges_SPIECEASI = mean(Count[DataType == "SPIEC-EASI"], na.rm = TRUE),
    sd_edges_SPIECEASI = sd(Count[DataType == "SPIEC-EASI"], na.rm = TRUE),
    n_SPIECEASI = sum(DataType == "SPIEC-EASI", na.rm = TRUE),
    
    # SPIEC-EASI no COV
    mean_edges_TRUE = mean(Count[DataType == "True"], na.rm = TRUE),
    sd_edges_TRUE = sd(Count[DataType == "True"], na.rm = TRUE),
    n_TRUE = sum(DataType == "True", na.rm = TRUE),
    
    # p-values
    p_value_LIMON_TRUE = if (n_LIMON > 0 & n_TRUE > 0) 
      tidy(exactRankTests::wilcox.exact(Count[DataType %in% c("LIMON", "True")] ~ 
                                          DataType[DataType %in% c("LIMON", "True")], exact = FALSE))$p.value else NA,
    p_value_LIMON_SPIECEASI = if (n_LIMON > 0 & n_SPIECEASI > 0) 
      tidy(exactRankTests::wilcox.exact(Count[DataType %in% c("LIMON", "SPIEC-EASI")] ~ 
                                          DataType[DataType %in% c("LIMON", "SPIEC-EASI")], 
                       exact = FALSE))$p.value else NA,
                       p_value_SPIECEASI_TRUE = if (n_SPIECEASI > 0 & n_TRUE > 0) 
      tidy(exactRankTests::wilcox.exact(Count[DataType %in% c("SPIEC-EASI", "True")] ~ 
                                          DataType[DataType %in% c("SPIEC-EASI", "True")], 
                       exact = FALSE))$p.value else NA
  ) %>%
  mutate(
    se_edges_LIMON = sd_edges_LIMON / sqrt(n_LIMON),
    se_edges_SPIECEASI = sd_edges_SPIECEASI / sqrt(n_SPIECEASI),
    se_edges_TRUE = sd_edges_TRUE / sqrt(n_TRUE),
    significance_LIMON_TRUE = sapply(p_value_LIMON_TRUE, p_value_sig),
    significance_LIMON_SPIECEASI = sapply(p_value_LIMON_SPIECEASI, p_value_sig),
    significance_SPIECEASI_TRUE = sapply(p_value_SPIECEASI_TRUE, p_value_sig)
  )

# Step 2: Make the plotting data
#################################################################
Summary_data_long <- Summary_data %>%
  dplyr::select(Connectance, starts_with("mean_edges"), starts_with("se_edges"), starts_with("significance")) %>%
  pivot_longer(cols = starts_with("mean_edges"), names_to = "DataType", values_to = "mean_edges") %>%
  mutate(DataType = case_when(
    str_detect(DataType, "LIMON") ~ "LIMON",
    str_detect(DataType, "SPIECEASI") ~ "SPIEC-EASI",
    str_detect(DataType, "TRUE") ~ "True"
  )) %>%
  pivot_longer(cols = starts_with("se_edges"), names_to = "DataType_se", values_to = "se_edges") %>%
  mutate(DataType_se = case_when(
    str_detect(DataType_se, "LIMON") ~ "LIMON",
    str_detect(DataType_se, "SPIECEASI") ~ "SPIEC-EASI",
    str_detect(DataType_se, "TRUE") ~ "True"
  )) %>%
  filter(DataType == DataType_se) %>%
  dplyr::select(-DataType_se)

# Add significance labels
Summary_data_long <- Summary_data_long %>%
  left_join(
    Summary_data %>% dplyr::select(Connectance, 
                                   significance_LIMON_TRUE, 
                                   significance_LIMON_SPIECEASI, 
                                   significance_SPIECEASI_TRUE),
    by = "Connectance"
  )
# Step 3: Final figure
#################################################################
ggplot(Summary_data_long, aes(x = Connectance, y = mean_edges, color = DataType)) +
  geom_line() +
  geom_point() +
  
  # Add Error bars
  geom_errorbar(aes(ymin = mean_edges - se_edges, 
                    ymax = mean_edges + se_edges), width = 0.01) +
  
    # Add a legend to describe all the colors
  scale_color_manual(name = "Network", 
                     values = c("LIMON" = "orange", 
                                "SPIEC-EASI" = "blue", 
                                "True" = "grey"),
                     labels = c("LIMON" = "LIMON", 
                                "SPIEC-EASI" = "SPIEC-EASI + Covariates", 
                                "True" = "SPIEC-EASI - No Covariates")) +
  
  # Use ggnewscale to define a new color scale after the first one
  ggnewscale::new_scale_color() +
  
  # Annotate LIMON vs TRUE significance level
  geom_text(aes(label = significance_LIMON_TRUE.x, y = (mean_edges + se_edges + 5), 
                color = factor("LIMON-True")), 
            size = 4, vjust = -0.5, check_overlap = TRUE, 
            data = Summary_data_long %>% filter(DataType == "LIMON")) + 
  
  # Annotate LIMON vs SPIEC-EASI significance level
  geom_text(aes(label = significance_LIMON_SPIECEASI.x, y = (mean_edges + se_edges + 15), 
                color = factor("LIMON-SPIECEASI")), 
            size = 4, vjust = -0.5, check_overlap = TRUE, 
            data = Summary_data_long %>% filter(DataType == "LIMON")) + 
  
  # Annotate SPIEC-EASI vs TRUE significance level
  geom_text(aes(label = significance_SPIECEASI_TRUE.x, y = (mean_edges + se_edges + 8), 
                color = factor("SPIECEASI-True")), 
            size = 4, vjust = -0.5, check_overlap = TRUE, 
            data = Summary_data_long %>% filter(DataType == "SPIEC-EASI")) + 
  
  # Add on labels and themes
  labs(x = "Connectance", y = "Total Network Edges") +
  scale_x_continuous(limits = c(0.10, 1.00), breaks = c(0.1,0.20,0.50,0.75,0.90))  +
  theme_classic() +
  theme(axis.text.x = element_text(family = "arial",color = "black", size = 18),
        axis.text.y = element_text(family = "arial",color = "black", size = 18),
        axis.title.x = element_text(family = "arial",color = "black", size = 14),
        axis.title.y = element_text(family = "arial",color = "black", size = 14),
        legend.text = element_text(size = 10),   # Increases legend text size
        legend.title = element_text(size = 12)) +
  
  # Add a legend to describe all the colors
  scale_color_manual(name = "Wilcoxon signficance", 
                     values = c("LIMON-True" = "darkorange2", 
                                "LIMON-SPIECEASI" = "black",
                                "SPIECEASI-True" = "deepskyblue"),
                     labels = c("LIMON-True" = "LIMON*No Cov", 
                                "LIMON-SPIECEASI" = "LIMON*SPIECEASI",
                                "SPIECEASI-True" = "SPIECEASI*No Cov"))

3.3 Strength of Recovered Edges

3.3.1 - Data Formatting

Read back in the raw data with no covariates, get the spearman correlation per time point (1,2,3,4,5) and per sample size level (10,20,50,75,100). We will then compare these strength of recovered edges to the edges recovered by our various tests above.

Raw10 <- read.csv(here("Data", "GLV_SimData", "Dataset_4","GLV_0.10.csv")) %>% 
                                          column_to_rownames("X") %>% 
                                          dplyr::select(-c(Time, ID, Sex, BMI, Age))
Raw20 <- read.csv(here("Data", "GLV_SimData", "Dataset_4","GLV_0.20.csv")) %>% 
                                          column_to_rownames("X") %>% 
                                          dplyr::select(-c(Time, ID, Sex, BMI, Age))
Raw50 <- read.csv(here("Data", "GLV_SimData", "Dataset_4","GLV_0.50.csv")) %>% 
                                          column_to_rownames("X") %>% 
                                          dplyr::select(-c(Time, ID, Sex, BMI, Age))
Raw75 <- read.csv(here("Data", "GLV_SimData", "Dataset_4","GLV_0.75.csv")) %>% 
                                          column_to_rownames("X") %>% 
                                          dplyr::select(-c(Time, ID, Sex, BMI, Age))
Raw100 <- read.csv(here("Data", "GLV_SimData", "Dataset_4","GLV_0.90.csv")) %>% 
                                          column_to_rownames("X") %>% 
                                          dplyr::select(-c(Time, ID, Sex, BMI, Age))
# Define sample sizes and corresponding data frames
connectance_size <- c(0.10, 0.20, 0.50, 0.75, 0.90)
data_frames <- list(Raw10, Raw20, Raw50, Raw75, Raw100)

# Initialize an empty list to store results for each sample size
cov_results <- list()

for (i in seq_along(connectance_size)) {
  df <- data_frames[[i]]
  sample_size <- connectance_size[i]
  
  df$Subject <- sub("_Time[0-9]+", "", rownames(df))
  df$Timepoint <- sub("Sbj[0-9]+_", "", rownames(df))
  df$Timepoint <- gsub("Time", "", df$Timepoint)
  
  # List of unique timepoints
  timepoints <- unique(df$Timepoint)
  
  # Initialize an empty data frame to store results
  result_df <- data.frame(Source = character(),
                          Sink = character(),
                          Covariance = numeric(),
                          Timepoint = character(),
                          stringsAsFactors = FALSE)
  
  # Loop through each timepoint
  for (time in timepoints) {
    # Subset data for the current timepoint
    df_time <- df %>% filter(Timepoint == time) %>% dplyr::select(-Subject, -Timepoint)
    
    # Calculate covariance matrix for the current timepoint
    cov_matrix <- cor(df_time, method = "spearman")
    
    # Convert the covariance matrix to a long format
    cov_long <- as.data.frame(as.table(cov_matrix))
    
    # Rename columns for clarity
    names(cov_long) <- c("Sink", "Source",  "Covariance")
    
    # Add timepoint information
    cov_long$Timepoint <- time
    
    # Combine with the result data frame
    result_df <- bind_rows(result_df, cov_long)
  }
  
  # Add sample size information
  result_df <- result_df %>% dplyr::rename("Time" ="Timepoint")
  result_df$Connectance <- sample_size
  
  # Store in the list
  cov_results[[i]] <- result_df
}

# Merge all results into one data frame
True_cov <- do.call(rbind, cov_results)

Merge together and add annotaiton for strength of edges detected
- the x axis will be a range from the “Covariance” column with edges between -1 - -0.5, -0.5 - -0.1, -0.1 - 0.1, 0.1 -0.5, 0.5 - 1.
- the y axis will be what percentage of the edges detected for that network/method fall into each of these categories

# Merge with my data
Strength_data <- merge(True_cov, Subject_sens_data_taxa, by.y = c("Source", "Sink", "Time", "Connectance"), all = TRUE)

# Add in columns based on what strength of association it detected
Strength_data <- Strength_data %>% 
  # for LIMON recovery
  mutate(LIMON_strength = case_when(
    !is.na(L_Edge_weight) & Covariance >= -1 & Covariance < -0.5 ~ "-1 - -0.5",
    !is.na(L_Edge_weight) & Covariance >= -0.5 & Covariance < -0.1 ~ "-0.5 - -0.1",
    !is.na(L_Edge_weight) & Covariance >= -0.1 & Covariance < 0.1 ~ "-0.1 - 0.1",
    !is.na(L_Edge_weight) & Covariance >= 0.1 & Covariance < 0.5 ~ "0.1 - 0.5",
    !is.na(L_Edge_weight) & Covariance >= 0.5 & Covariance <= 1 ~ "0.5 - 1",
    TRUE ~ as.character(NA)  
  )) %>% 
  # for SPIEC-EASI recovery
  mutate(SPIECEASI_strength = case_when(
    !is.na(Cov_Edge_weight) & Covariance >= -1 & Covariance < -0.5 ~ "-1 - -0.5",
    !is.na(Cov_Edge_weight) & Covariance >= -0.5 & Covariance < -0.1 ~ "-0.5 - -0.1",
    !is.na(Cov_Edge_weight) & Covariance >= -0.1 & Covariance < 0.1 ~ "-0.1 - 0.1",
    !is.na(Cov_Edge_weight) & Covariance >= 0.1 & Covariance < 0.5 ~ "0.1 - 0.5",
    !is.na(Cov_Edge_weight) & Covariance >= 0.5 & Covariance <= 1 ~ "0.5 - 1",
    TRUE ~ as.character(NA)  
  ))

# Split into True Positives Dataset and False Positives Datasets
True_pos_raw <- Strength_data %>% filter(!is.na(O_Edge_weight)) %>%
                                  filter(!(is.na(L_Edge_weight) & is.na(Cov_Edge_weight)))
False_pos_raw <- Strength_data %>% filter(is.na(O_Edge_weight)) %>%
                                  filter(!(is.na(L_Edge_weight) & is.na(Cov_Edge_weight)))

3.3.2 - Statistics

True Positives

False Positives

4 - Notifications

#system("say Your Silly code finished!")
LS0tCnRpdGxlOiAiRGF0YXNldCA0IC0gU2Vuc2l0aXZpdHkgb2YgTmV0d29yayBTcGFyc2l0eSIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKSGVyZSB0ZXN0IHRoZSBzdHJlbmd0aCBvZiByZWNvdmVyeSBvZiBlZGdlcyBvZiB0aGUgbmV0d29ya3MgZm9yIENvbm5lY3RuYWNlCgoKSGVyZSB0ZXN0IHRoZSBzdHJlbmd0aCBvZiByZWNvdmVyeSBvZiBlZGdlcyBvZiB0aGUgbmV0d29ya3Mgb24gY292YXJpYXRlIGluZmxhdGVkIGRhdGEgcGFzc2VkIHRvIExJTU9OIGFuZCBwYXNzZWQgdG8gU1BJRUMtRUFTSS4gVGhlc2Ugd2lsbCBiZSBjb21wYXJlZCB0byBub24tY292YXJpYXRlIGluZmxhdGVkIGRhdGEgcGFzc2VkIHRvIFNQSUVDLUVBU0kuIFdlIHV0aWxpemUgdGhlIExJTU9OIFNQSUVDLUVBU0kgZnVuY3Rpb25zIHRvIGl0ZXJhdGl2ZWx5IGdlbmVyYXRlIHRoZSBuZXR3b3JrcyBmb3IgY292YXJpYXRlIGRhdGEgcGFzc2VkIHRvIFNQSUVDLUVBU0ksIGJ1dCBieXBhc3MgdGhlIGxpbmVhciBtaXhlZCBlZmZlY3QgbW9kZWwgZGlzdHJpYnV0aW9uIGZpdHRpbmcgc3RlcCBvZiBMSU1PTi4gVGhpcyBpcyByZXBlYXRlZCA1IHRpbWVzIGZvciBpbmNyZWFzZSBHTFYgY29ubmVjdGFuY2Ugb2YgMC4xMCwgMC4yMCwgMC41MCwgMC43NSwgYW5kIDAuOS4gCgoKIyAxLiBMb2FkIExpYnJhcnkKKioqIApQYWNrYWdlcyByZXF1aXJlZCB0byBydW4gdGhlIHNjcmlwdApgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoaWdyYXBoKQpsaWJyYXJ5KE5CWklNTSkKbGlicmFyeShTcGllY0Vhc2kpCmxpYnJhcnkoTElNT04pCmxpYnJhcnkoaGVyZSkKbGlicmFyeShsbWU0KQpsaWJyYXJ5KE1hdHJpeCkKbGlicmFyeSh0c2NvdW50KQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShNQVNTKQpsaWJyYXJ5KG1hdHJpeGNhbGMpCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KGRldnRvb2xzKQpsaWJyYXJ5KG1pYVNpbSkKbGlicmFyeShyZXNoYXBlMikKbGlicmFyeShnZ3B1YnIpCmxpYnJhcnkoYnJvb20pCmxpYnJhcnkoZ2duZXdzY2FsZSkKbGlicmFyeShjb2luKQpgYGAKCiMjIDEuMiAtIEZ1bmN0aW9ucwoKVGhpcyBmdW5jdGlvbiB3aWxsIGJlIHVzZWQgdG8gZGVmaW5lIHdoaWNoIGVkZ2VzIGFyZSBjb25zaXN0ZW50IGFjcm9zcyBkaWZmZXJlbnQgbmV0d29ya3MKYGBge3J9CmVkZ2VfcmVjb3ZlcnkgPC0gZnVuY3Rpb24oZGF0YSkgewogIGRhdGEgPC0gZGF0YSAlPiUKICAgIG11dGF0ZSgKICAgICAgUmVjb3ZlcnkgPSBjYXNlX3doZW4oCiAgICAgICAgIWlzLm5hKExfRWRnZV93ZWlnaHQpICYgIWlzLm5hKE9fRWRnZV93ZWlnaHQpICYgIWlzLm5hKENvdl9FZGdlX3dlaWdodCkgJgogICAgICAgICAgKExfRWRnZV93ZWlnaHQgIT0gMCAmIE9fRWRnZV93ZWlnaHQgIT0gMCAmIENvdl9FZGdlX3dlaWdodCAhPSAwKSB+IDMsCiAgICAgICAgIWlzLm5hKE9fRWRnZV93ZWlnaHQpICYgIWlzLm5hKENvdl9FZGdlX3dlaWdodCkgJgogICAgICAgICAgKE9fRWRnZV93ZWlnaHQgIT0gMCAmIENvdl9FZGdlX3dlaWdodCAhPSAwKSB+IDIsCiAgICAgICAgIWlzLm5hKExfRWRnZV93ZWlnaHQpICYgIWlzLm5hKE9fRWRnZV93ZWlnaHQpICYKICAgICAgICAgIChMX0VkZ2Vfd2VpZ2h0ICE9IDAgJiBPX0VkZ2Vfd2VpZ2h0ICE9IDApIH4gMSwKICAgICAgICBUUlVFIH4gMAogICAgICApCiAgICApCiAgCiAgZGF0YQp9CmBgYAoKCgojIDIuIE1vZGVsIFRlc3RpbmcKKioqCgoKIyMgMi4xIC0gQ29ubmVjdGFuY2UgPSAwLjEwCgojIyMgMi4xLjEgLSBMSU1PTiBvbiBDb3ZhcmlhdGUgSW5mbGF0ZWQgRGF0YQpSdW4gdGhlIExJTU9OIGNvdmFyaWF0ZSBjb3JyZWN0aW9uIGFuZCBuZXR3b3JrIGluZmVyZW5jZSBzdGVwcyBvbiBDb3ZhcmlhdGUgaW5mbGF0ZWQgZGF0YS4gVGhlc2UgZWRnZXMgd2lsbCBiZSByZXF1aXJlZCBhcyBMX0VkZ2VzICAKCk1ha2UgTElNT04gT2JqZWN0CmBgYHtyfQpTaW1EYXRhIDwtIHJlYWQuY3N2KGhlcmUoIkRhdGEiLCAiR0xWX1NpbURhdGEiLCAiRGF0YXNldF80IiwiR0xWX0Nvdl8wLjEwLmNzdiIpKQoKU2ltRGF0YSA8LSBjb2x1bW5fdG9fcm93bmFtZXMoU2ltRGF0YSwgIlgiKQoKIyBtZXRhZGF0YQptZXRhX2RhdGEgPC0gU2ltRGF0YVssMTo1XQptZXRhX2RhdGEkVGltZSA8LSBhcy5udW1lcmljKG1ldGFfZGF0YSRUaW1lKQptZXRhX2RhdGEkQk1JIDwtIGFzLm51bWVyaWMobWV0YV9kYXRhJEJNSSkKbWV0YV9kYXRhJEFnZSA8LSBhcy5udW1lcmljKG1ldGFfZGF0YSRBZ2UpCgojIE9wdGlvbiB0byBzaG9ydGVuIHRvIHJ1biBmYXN0ZXIKbGltb25fbWV0YSA8LSBtZXRhX2RhdGEgIyU+JSBmaWx0ZXIoVGltZSAlaW4lIGMoMSwyLDMsNCw1KSkKCiMgQ291bnQgZGF0YQpsaW1vbl9jb3VudHMgPC0gYXMubWF0cml4KFNpbURhdGFbLDY6NTVdKQoKIyBzb3J0IHJvd3MKbGltb25fY291bnRzIDwtIGxpbW9uX2NvdW50c1tyb3duYW1lcyhsaW1vbl9tZXRhKSxdCgogIApMX29iaiA8LSBMSU1PTl9PYmooQ291bnRzID0gbGltb25fY291bnRzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgU2FtcGxlRGF0YSA9IGxpbW9uX21ldGEpCmBgYAoKCgoKCkRpc3RyaWJ1dGlvbiBGaXR0aW5nIGFuZCBjaGVjawpgYGB7cn0KIyBTZXQgc2VlZApzZXQuc2VlZCgxMjM0NSkKIyBGaXQgdGhlIGRpc3RyaWJ1dGlvbi9yZW1vdmUgY292YXJpYXRlcwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKTF9vYmoyIDwtIExJTU9OX0Rpc3RyRml0KE9iaiA9IExfb2JqLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgVGltZSA9ICJUaW1lIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFN1YmplY3QgPSAiSUQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgQ292YXJpYXRlcyA9IGMoIlNleCIsICJCTUkiLCAiQWdlIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsID0gIlNleCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3RyaWJ1dGlvbiA9ICJMTU0iKQoKYGBgCgoKCgoKTmV0d29ya3Mgb2YgTElNT04gRGF0YQpgYGB7cn0Kc2V0LnNlZWQoMTIzNDUpCnBzZWVkIDwtIGxpc3QocmVwLm51bT01MCwgc2VlZD0xMDAxMCkKIyBTUElFQy1FQVNJIHBlciB0aW1lCkxfb2JqMyA8LSBMSU1PTl9OZXRJbmZfVGltZShPYmogPSBMX29iajIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJnbGFzc28iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWwuY3JpdGVyaW9uID0gImJzdGFycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFtYmRhLm1pbi5yYXRpbyA9IDAuMDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsc2FyLnNlbGVjdD1UUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxzYXIucGFyYW1zPXBzZWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5sYW1iZGEgPSAyMDApCgoKIyBQcmludCBOZXR3b3JrcwpMX29iajQgPC0gTElNT05fRWRnZXNfTmV0d29ya3MoTF9vYmozLCB0aHJlc2hvbGQgPSAwLjAwMDIsIHZlcnRleC5zaXplID0gMywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcnRleC5sYWJlbC5jZXggPSA4LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVydGV4LmxhYmVsLmNvbG9yID0gImJsYWNrIikKYGBgCgoKCiMjIyAyLjEuMiAtIFRydWUgTmV0d29ya3MgLSBubyBDb3ZhcmlhdGVzICAKClNQSUVDLUVBU0kgTmV0d29ya3Mgb2YgdGhlIFJhdyBEYXRhIHdpdGhvdXQgY292YXJpYXRlcy4gVGhlc2Ugd2lsbCBiZSBsYWJlbGVkIGFzIE9fRWRnZXMgKGZvciBvcmlnaW5hbCBlZGdlcykuIFdlIHJlYWQgaW4gdGhlIGRhdGEgYW5kIHRoZW4gYXNzaWduIGl0IHRvIGEgZG93bnN0cmVhbSBMSU1PbiBvYmplY3QgdG8gcnVuIFNQSUVDLUVBU0kgYWNyb3NzIGFsbCB0aGUgdGltZXBvaW50cyBidXQgYnlwYXNzaW5nIHRoZSBuZXR3b3JrIGluZmVyZW5jZSBzdGVwCmBgYHtyfQojIE1ha2UgZmFrZSBMSU1PTiBPYmplY3QgdG8gUGFzcyB0byBOZXR3b3JrIEluZmVyZW5jZSBTdGVwcwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCm9yaWdpbmFsX2RhdGEgPC0gcmVhZC5jc3YoaGVyZSgiRGF0YSIsICJHTFZfU2ltRGF0YSIsICJEYXRhc2V0XzQiLCJHTFZfMC4xMC5jc3YiKSkKb3JpZ2luYWxfZGF0YSA8LSBvcmlnaW5hbF9kYXRhICU+JSBjb2x1bW5fdG9fcm93bmFtZXMoIlgiKQoKIyBMb29wIHRocm91Z2ggdGhlIHRpbWUgcG9pbnRzLCBmaWx0ZXIgZGF0YSwgYW5kIHJlbW92ZSBtZXRhZGF0YQpjb3VudHNfbGlzdCA8LSBsaXN0KCkKZm9yIChpIGluIDE6MTApIHsKICBmaWx0ZXJlZF9jb3VudHMgPC0gb3JpZ2luYWxfZGF0YSAlPiUgZmlsdGVyKFRpbWUgPT0gaSkKICBjb3VudHNfbGlzdFtbaV1dIDwtIHJvdW5kKGZpbHRlcmVkX2NvdW50c1ssIDY6NTVdKQp9CgojIEluaXRpYWxpemUgTF9vYmpfc2ltMgpMX29ial9zaW0yIDwtIExfb2JqMgoKIyBBc3NpZ24gdGhlIHByb2Nlc3NlZCBkYXRhIHRvIHRoZSBjb3JyZXNwb25kaW5nIHNsb3RzIGluIExfb2JqX3NpbTIKZm9yIChpIGluIDE6MTApIHsKICBMX29ial9zaW0yW1siQ29ycmVjdGVkX0NvdW50c19UaW1lIl1dW1twYXN0ZTAoIkNvcnJlY3RlZF9Db3VudHNfIiwgaSldXSA8LSBjb3VudHNfbGlzdFtbaV1dCn0KCgojIE5ldHdvcmsgSW5mZXJlbmNlCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTZXQgc2VlZApzZXQuc2VlZCgxMjM0NSkKcHNlZWQgPC0gbGlzdChyZXAubnVtPTUwLCBzZWVkPTEwMDEwKQojIFNQSUVDLUVBU0kgcGVyIHRpbWUKTF9vYmpfc2ltMyA8LSBMSU1PTl9OZXRJbmZfVGltZShPYmogPSBMX29ial9zaW0yLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiZ2xhc3NvIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsLmNyaXRlcmlvbiA9ICJic3RhcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhbWJkYS5taW4ucmF0aW8gPSAwLjAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB1bHNhci5zZWxlY3Q9VFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsc2FyLnBhcmFtcz1wc2VlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBubGFtYmRhID0gMjAwKQoKCiMgUHJpbnQgTmV0d29ya3MKTF9vYmpfc2ltNCA8LSBMSU1PTl9FZGdlc19OZXR3b3JrcyhMX29ial9zaW0zLCB0aHJlc2hvbGQgPSAwLjAwMDIsIHZlcnRleC5zaXplID0gMywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcnRleC5sYWJlbC5jZXggPSA4LCB2ZXJ0ZXgubGFiZWwuY29sb3IgPSAiYmxhY2siKQoKYGBgCgoKIyMjIDIuMS4zIC0gU1BJRUMtRUFTSSBvbiBDb3ZhcmlhdGUgRGF0YSAgClNpbWlsYXIgdG8gcHJvY2VzcyBmb3Igb3JpZ2luYWwgbmV0d29ya3MsIGV4Y2VwdCBub3cgd2Ugd2lsbCBydW4gaXQgb24gY292YXJpYXRlIGNvbmZsYXRlZCBkYXRhLiBUaGVzZSB3aWxsIGJlIGxhYmVsZWQgYXMgQ292X0VkZ2VzCmBgYHtyfQojIE1ha2UgZmFrZSBMSU1PTiBPYmplY3QgdG8gUGFzcyB0byBOZXR3b3JrIEluZmVyZW5jZSBTdGVwcwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCm9yaWdpbmFsX2RhdGEgPC0gcmVhZC5jc3YoaGVyZSgiRGF0YSIsICJHTFZfU2ltRGF0YSIsICJEYXRhc2V0XzQiLCJHTFZfQ292XzAuMTAuY3N2IikpCm9yaWdpbmFsX2RhdGEgPC0gb3JpZ2luYWxfZGF0YSAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCJYIikKCiMgTG9vcCB0aHJvdWdoIHRoZSB0aW1lIHBvaW50cywgZmlsdGVyIGRhdGEsIGFuZCByZW1vdmUgbWV0YWRhdGEKY291bnRzX2xpc3QgPC0gbGlzdCgpCmZvciAoaSBpbiAxOjEwKSB7CiAgZmlsdGVyZWRfY291bnRzIDwtIG9yaWdpbmFsX2RhdGEgJT4lIGZpbHRlcihUaW1lID09IGkpCiAgY291bnRzX2xpc3RbW2ldXSA8LSByb3VuZChmaWx0ZXJlZF9jb3VudHNbLCA2OjU1XSkKfQoKIyBJbml0aWFsaXplIExfb2JqX3NpbTIKTF9vYmpfc2ltMiA8LSBMX29iajIKCiMgQXNzaWduIHRoZSBwcm9jZXNzZWQgZGF0YSB0byB0aGUgY29ycmVzcG9uZGluZyBzbG90cyBpbiBMX29ial9zaW0yCmZvciAoaSBpbiAxOjEwKSB7CiAgTF9vYmpfc2ltMltbIkNvcnJlY3RlZF9Db3VudHNfVGltZSJdXVtbcGFzdGUwKCJDb3JyZWN0ZWRfQ291bnRzXyIsIGkpXV0gPC0gY291bnRzX2xpc3RbW2ldXQp9CgoKCiMgTmV0d29yayBJbmZlcmVuY2UKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNldCBzZWVkCnNldC5zZWVkKDEyMzQ1KQpwc2VlZCA8LSBsaXN0KHJlcC5udW09NTAsIHNlZWQ9MTAwMTApCiMgU1BJRUMtRUFTSSBwZXIgdGltZQpMX29ial9zaW0zIDwtIExJTU9OX05ldEluZl9UaW1lKE9iaiA9IExfb2JqX3NpbTIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJnbGFzc28iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWwuY3JpdGVyaW9uID0gImJzdGFycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFtYmRhLm1pbi5yYXRpbyA9IDAuMDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsc2FyLnNlbGVjdD1UUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxzYXIucGFyYW1zPXBzZWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5sYW1iZGEgPSAyMDApCgoKIyBQcmludCBOZXR3b3JrcwpMX29ial9zaW01IDwtIExJTU9OX0VkZ2VzX05ldHdvcmtzKExfb2JqX3NpbTMsIHRocmVzaG9sZCA9IDAuMDAwMiwgdmVydGV4LnNpemUgPSAzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVydGV4LmxhYmVsLmNleCA9IDgsIHZlcnRleC5sYWJlbC5jb2xvciA9ICJibGFjayIpCgoKCmBgYAoKCgojIyMgMi4xLjQgLSBNZXJnZSBPdXRjb21lcwpDYWxjdWxhdGUgdGhlIHNpbWlsYXIgZWRnZXMgYWNyb3NzIHRoZSB0aHJlZSBkYXRhIHR5cGVzCmBgYHtyfQojIExJTU9OIERhdGEgLSBFeHRyYWN0IEVkZ2VzCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIEluaXRpYWxpemUgYW4gZW1wdHkgbGlzdCB0byBzdG9yZSB0aGUgZGF0YSBmcmFtZXMKTF9FZGdlc19MaXN0IDwtIGxpc3QoKQoKIyBMb29wIHRocm91Z2ggdGhlIGVkZ2UgdGFibGVzIGFuZCBwcm9jZXNzIGVhY2ggb25lCmZvciAoaSBpbiAxOjEwKSB7CiAgZWRnZV90YWJsZSA8LSBhcy5kYXRhLmZyYW1lKExfb2JqNFtbIkVkZ2VfVGFibGUiXV1bW3Bhc3RlMCgiRWRnZV9UYWJsZV8iLCBpKV1dKQogICMgQ2hlY2sgaWYgdGhlIGVkZ2UgdGFibGUgaXMgZW1wdHkKICBpZiAobnJvdyhlZGdlX3RhYmxlKSA9PSAwKSB7CiAgICAjIENyZWF0ZSBhIGRhdGEgZnJhbWUgd2l0aCBOQSB2YWx1ZXMKICAgIGVkZ2VfdGFibGUgPC0gZGF0YS5mcmFtZShFZGdlX3dlaWdodCA9IE5BLCBUaW1lID0gaSkKICB9IGVsc2UgewogICAgIyBSZW5hbWUgdGhlIGNvbHVtbiBhbmQgYWRkIHRoZSBUaW1lIGNvbHVtbgogICAgZWRnZV90YWJsZSA8LSBlZGdlX3RhYmxlICU+JSBkcGx5cjo6cmVuYW1lKCJMX0VkZ2Vfd2VpZ2h0Ij0iRWRnZV93ZWlnaHQiKQogICAgZWRnZV90YWJsZSRUaW1lIDwtIGkKICB9CiAgIyBBcHBlbmQgdGhlIHByb2Nlc3NlZCB0YWJsZSB0byB0aGUgbGlzdAogIExfRWRnZXNfTGlzdFtbaV1dIDwtIGVkZ2VfdGFibGUKfQojIENvbWJpbmUgYWxsIHRoZSBwcm9jZXNzZWQgdGFibGVzIGludG8gYSBzaW5nbGUgZGF0YSBmcmFtZQpMX0VkZ2VzIDwtIGJpbmRfcm93cyhMX0VkZ2VzX0xpc3QpCgojIE9yaWdpbmFsIERhdGEgKyBDb3YgLSBFeHRyYWN0IGVkZ2VzCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIEluaXRpYWxpemUgYW4gZW1wdHkgbGlzdCB0byBzdG9yZSB0aGUgZGF0YSBmcmFtZXMKQ292X0VkZ2VzX0xpc3QgPC0gbGlzdCgpCgojIExvb3AgdGhyb3VnaCB0aGUgZWRnZSB0YWJsZXMgYW5kIHByb2Nlc3MgZWFjaCBvbmUKZm9yIChpIGluIDE6MTApIHsKICBlZGdlX3RhYmxlIDwtIGFzLmRhdGEuZnJhbWUoTF9vYmpfc2ltNVtbIkVkZ2VfVGFibGUiXV1bW3Bhc3RlMCgiRWRnZV9UYWJsZV8iLCBpKV1dKQogICMgQ2hlY2sgaWYgdGhlIGVkZ2UgdGFibGUgaXMgZW1wdHkKICBpZiAobnJvdyhlZGdlX3RhYmxlKSA9PSAwKSB7CiAgICAjIENyZWF0ZSBhIGRhdGEgZnJhbWUgd2l0aCBOQSB2YWx1ZXMKICAgIGVkZ2VfdGFibGUgPC0gZGF0YS5mcmFtZShFZGdlX3dlaWdodCA9IE5BLCBUaW1lID0gaSkKICB9IGVsc2UgewogICAgIyBSZW5hbWUgdGhlIGNvbHVtbiBhbmQgYWRkIHRoZSBUaW1lIGNvbHVtbgogICAgZWRnZV90YWJsZSA8LSBlZGdlX3RhYmxlICU+JSBkcGx5cjo6cmVuYW1lKCJDb3ZfRWRnZV93ZWlnaHQiPSJFZGdlX3dlaWdodCIpCiAgICBlZGdlX3RhYmxlJFRpbWUgPC0gaQogIH0KICAjIEFwcGVuZCB0aGUgcHJvY2Vzc2VkIHRhYmxlIHRvIHRoZSBsaXN0CiAgQ292X0VkZ2VzX0xpc3RbW2ldXSA8LSBlZGdlX3RhYmxlCn0KIyBDb21iaW5lIGFsbCB0aGUgcHJvY2Vzc2VkIHRhYmxlcyBpbnRvIGEgc2luZ2xlIGRhdGEgZnJhbWUKQ292X0VkZ2VzIDwtIGJpbmRfcm93cyhDb3ZfRWRnZXNfTGlzdCkKCiMgT3JpZ2luYWwgRGF0YSAtIEV4dHJhY3QgRWRnZXMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBJbml0aWFsaXplIGFuIGVtcHR5IGxpc3QgdG8gc3RvcmUgdGhlIGRhdGEgZnJhbWVzCk9fRWRnZXNfTGlzdCA8LSBsaXN0KCkKCiMgTG9vcCB0aHJvdWdoIHRoZSBlZGdlIHRhYmxlcyBhbmQgcHJvY2VzcyBlYWNoIG9uZQpmb3IgKGkgaW4gMToxMCkgewogIGVkZ2VfdGFibGUgPC0gYXMuZGF0YS5mcmFtZShMX29ial9zaW00W1siRWRnZV9UYWJsZSJdXVtbcGFzdGUwKCJFZGdlX1RhYmxlXyIsIGkpXV0pCiAgIyBDaGVjayBpZiB0aGUgZWRnZSB0YWJsZSBpcyBlbXB0eQogIGlmIChucm93KGVkZ2VfdGFibGUpID09IDApIHsKICAgICMgQ3JlYXRlIGEgZGF0YSBmcmFtZSB3aXRoIE5BIHZhbHVlcwogICAgZWRnZV90YWJsZSA8LSBkYXRhLmZyYW1lKEVkZ2Vfd2VpZ2h0ID0gTkEsIFRpbWUgPSBpKQogIH0gZWxzZSB7CiAgICAjIFJlbmFtZSB0aGUgY29sdW1uIGFuZCBhZGQgdGhlIFRpbWUgY29sdW1uCiAgICBlZGdlX3RhYmxlIDwtIGVkZ2VfdGFibGUgJT4lIGRwbHlyOjpyZW5hbWUoIk9fRWRnZV93ZWlnaHQiPSJFZGdlX3dlaWdodCIpCiAgICBlZGdlX3RhYmxlJFRpbWUgPC0gaQogIH0KICAjIEFwcGVuZCB0aGUgcHJvY2Vzc2VkIHRhYmxlIHRvIHRoZSBsaXN0CiAgT19FZGdlc19MaXN0W1tpXV0gPC0gZWRnZV90YWJsZQp9CiMgQ29tYmluZSBhbGwgdGhlIHByb2Nlc3NlZCB0YWJsZXMgaW50byBhIHNpbmdsZSBkYXRhIGZyYW1lCk9fRWRnZXMgPC0gYmluZF9yb3dzKE9fRWRnZXNfTGlzdCkKCiMgQmluZCBkYXRhIHRvZ2V0aGVyCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCk1lcmdlZF9UMSA8LSBtZXJnZShMX0VkZ2VzLCBPX0VkZ2VzLCBieS55ID0gYygiU291cmNlIiwgIlNpbmsiLCAiVGltZSIpLCBhbGw9VFJVRSkKTWVyZ2VkX1QxIDwtIG1lcmdlKE1lcmdlZF9UMSwgQ292X0VkZ2VzLCBieS55ID0gYygiU291cmNlIiwgIlNpbmsiLCAiVGltZSIpLCBhbGw9VFJVRSkKCgojIENvbXBhcmlzb24gR3JhcGggRGF0YQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyBJZGVudGlmeSB0cnVlIGVkZ2VzCk1lcmdlZF9UMTAgPC0gTWVyZ2VkX1QxICU+JQogIGVkZ2VfcmVjb3ZlcnkoKSAKCiMgQWRkIENvbm5lY3RhbmNlIApNZXJnZWRfVDEwJENvbm5lY3RhbmNlIDwtIDAuMTAKYGBgCgoKCgoKIyMgMi4yIC0gQ29ubmVjdGFuY2UgPSAwLjIwCgojIyMgMi4yLjEgLSBMSU1PTiBvbiBDb3ZhcmlhdGUgSW5mbGF0ZWQgRGF0YQpSdW4gdGhlIExJTU9OIGNvdmFyaWF0ZSBjb3JyZWN0aW9uIGFuZCBuZXR3b3JrIGluZmVyZW5jZSBzdGVwcyBvbiBDb3ZhcmlhdGUgaW5mbGF0ZWQgZGF0YS4gVGhlc2UgZWRnZXMgd2lsbCBiZSByZXF1aXJlZCBhcyBMX0VkZ2VzICAKCgpNYWtlIExJTU9OIE9iamVjdApgYGB7cn0KU2ltRGF0YSA8LSByZWFkLmNzdihoZXJlKCJEYXRhIiwgIkdMVl9TaW1EYXRhIiwgIkRhdGFzZXRfNCIsIkdMVl9Db3ZfMC4yMC5jc3YiKSkKU2ltRGF0YSA8LSBjb2x1bW5fdG9fcm93bmFtZXMoU2ltRGF0YSwgIlgiKQoKIyBtZXRhZGF0YQptZXRhX2RhdGEgPC0gU2ltRGF0YVssMTo1XQptZXRhX2RhdGEkVGltZSA8LSBhcy5udW1lcmljKG1ldGFfZGF0YSRUaW1lKQptZXRhX2RhdGEkQk1JIDwtIGFzLm51bWVyaWMobWV0YV9kYXRhJEJNSSkKbWV0YV9kYXRhJEFnZSA8LSBhcy5udW1lcmljKG1ldGFfZGF0YSRBZ2UpCgojIE9wdGlvbiB0byBzaG9ydGVuIHRvIHJ1biBmYXN0ZXIKbGltb25fbWV0YSA8LSBtZXRhX2RhdGEgIyU+JSBmaWx0ZXIoVGltZSAlaW4lIGMoMSwyLDMsNCw1KSkKCiMgQ291bnQgZGF0YQpsaW1vbl9jb3VudHMgPC0gYXMubWF0cml4KFNpbURhdGFbLDY6NTVdKQoKIyBzb3J0IHJvd3MKbGltb25fY291bnRzIDwtIGxpbW9uX2NvdW50c1tyb3duYW1lcyhsaW1vbl9tZXRhKSxdCgogIApMX29iaiA8LSBMSU1PTl9PYmooQ291bnRzID0gbGltb25fY291bnRzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgU2FtcGxlRGF0YSA9IGxpbW9uX21ldGEpCmBgYAoKCkRpc3RyaWJ1dGlvbiBGaXR0aW5nIGFuZCBjaGVjawpgYGB7cn0KIyBTZXQgc2VlZApzZXQuc2VlZCgxMjM0NSkKIyBGaXQgdGhlIGRpc3RyaWJ1dGlvbi9yZW1vdmUgY292YXJpYXRlcwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKTF9vYmoyIDwtIExJTU9OX0Rpc3RyRml0KE9iaiA9IExfb2JqLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgVGltZSA9ICJUaW1lIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFN1YmplY3QgPSAiSUQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgQ292YXJpYXRlcyA9IGMoIlNleCIsICJCTUkiLCAiQWdlIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsID0gIlNleCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3RyaWJ1dGlvbiA9ICJMTU0iKQoKYGBgCgoKTmV0d29ya3Mgb2YgTElNT04gRGF0YQpgYGB7cn0KIyBTZXQgc2VlZApzZXQuc2VlZCgxMjM0NSkKcHNlZWQgPC0gbGlzdChyZXAubnVtPTUwLCBzZWVkPTEwMDEwKQoKIyBTUElFQy1FQVNJIHBlciB0aW1lCkxfb2JqMyA8LSBMSU1PTl9OZXRJbmZfVGltZShPYmogPSBMX29iajIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJnbGFzc28iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWwuY3JpdGVyaW9uID0gImJzdGFycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFtYmRhLm1pbi5yYXRpbyA9IDAuMDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsc2FyLnNlbGVjdD1UUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxzYXIucGFyYW1zPXBzZWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5sYW1iZGEgPSAyMDApCgoKIyBQcmludCBOZXR3b3JrcwpMX29iajQgPC0gTElNT05fRWRnZXNfTmV0d29ya3MoTF9vYmozLCB0aHJlc2hvbGQgPSAwLjAwMDIsIHZlcnRleC5zaXplID0gMywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcnRleC5sYWJlbC5jZXggPSA4LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVydGV4LmxhYmVsLmNvbG9yID0gImJsYWNrIikKYGBgCgojIyMgMi4yLjIgLSBUcnVlIE5ldHdvcmtzIC0gbm8gQ292YXJpYXRlcyAgCgpTUElFQy1FQVNJIE5ldHdvcmtzIG9mIHRoZSBSYXcgRGF0YSB3aXRob3V0IGNvdmFyaWF0ZXMuIFRoZXNlIHdpbGwgYmUgbGFiZWxlZCBhcyBPX0VkZ2VzIChmb3Igb3JpZ2luYWwgZWRnZXMpLiBXZSByZWFkIGluIHRoZSBkYXRhIGFuZCB0aGVuIGFzc2lnbiBpdCB0byBhIGRvd25zdHJlYW0gTElNT24gb2JqZWN0IHRvIHJ1biBTUElFQy1FQVNJIGFjcm9zcyBhbGwgdGhlIHRpbWVwb2ludHMgYnV0IGJ5cGFzc2luZyB0aGUgbmV0d29yayBpbmZlcmVuY2Ugc3RlcApgYGB7cn0KIyBNYWtlIGZha2UgTElNT04gT2JqZWN0IHRvIFBhc3MgdG8gTmV0d29yayBJbmZlcmVuY2UgU3RlcHMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpvcmlnaW5hbF9kYXRhIDwtIHJlYWQuY3N2KGhlcmUoIkRhdGEiLCAiR0xWX1NpbURhdGEiLCAiRGF0YXNldF80IiwiR0xWXzAuMjAuY3N2IikpCm9yaWdpbmFsX2RhdGEgPC0gb3JpZ2luYWxfZGF0YSAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCJYIikKCiMgTG9vcCB0aHJvdWdoIHRoZSB0aW1lIHBvaW50cywgZmlsdGVyIGRhdGEsIGFuZCByZW1vdmUgbWV0YWRhdGEKY291bnRzX2xpc3QgPC0gbGlzdCgpCmZvciAoaSBpbiAxOjEwKSB7CiAgZmlsdGVyZWRfY291bnRzIDwtIG9yaWdpbmFsX2RhdGEgJT4lIGZpbHRlcihUaW1lID09IGkpCiAgY291bnRzX2xpc3RbW2ldXSA8LSByb3VuZChmaWx0ZXJlZF9jb3VudHNbLCA2OjU1XSkKfQoKIyBJbml0aWFsaXplIExfb2JqX3NpbTIKTF9vYmpfc2ltMiA8LSBMX29iajIKCiMgQXNzaWduIHRoZSBwcm9jZXNzZWQgZGF0YSB0byB0aGUgY29ycmVzcG9uZGluZyBzbG90cyBpbiBMX29ial9zaW0yCmZvciAoaSBpbiAxOjEwKSB7CiAgTF9vYmpfc2ltMltbIkNvcnJlY3RlZF9Db3VudHNfVGltZSJdXVtbcGFzdGUwKCJDb3JyZWN0ZWRfQ291bnRzXyIsIGkpXV0gPC0gY291bnRzX2xpc3RbW2ldXQp9CgoKIyBOZXR3b3JrIEluZmVyZW5jZQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU2V0IHNlZWQKc2V0LnNlZWQoMTIzNDUpCnBzZWVkIDwtIGxpc3QocmVwLm51bT01MCwgc2VlZD0xMDAxMCkKIyBTUElFQy1FQVNJIHBlciB0aW1lCkxfb2JqX3NpbTMgPC0gTElNT05fTmV0SW5mX1RpbWUoT2JqID0gTF9vYmpfc2ltMiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImdsYXNzbyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbC5jcml0ZXJpb24gPSAiYnN0YXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYW1iZGEubWluLnJhdGlvID0gMC4wMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxzYXIuc2VsZWN0PVRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB1bHNhci5wYXJhbXM9cHNlZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmxhbWJkYSA9IDIwMCkKCgojIFByaW50IE5ldHdvcmtzCkxfb2JqX3NpbTQgPC0gTElNT05fRWRnZXNfTmV0d29ya3MoTF9vYmpfc2ltMywgdGhyZXNob2xkID0gMC4wMDAyLCB2ZXJ0ZXguc2l6ZSA9IDMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJ0ZXgubGFiZWwuY2V4ID0gOCwgdmVydGV4LmxhYmVsLmNvbG9yID0gImJsYWNrIikKCgoKYGBgCgoKCiMjIyAyLjIuMyAtIFNQSUVDLUVBU0kgb24gQ292YXJpYXRlIERhdGEgIApTaW1pbGFyIHRvIHByb2Nlc3MgZm9yIG9yaWdpbmFsIG5ldHdvcmtzLCBleGNlcHQgbm93IHdlIHdpbGwgcnVuIGl0IG9uIGNvdmFyaWF0ZSBjb25mbGF0ZWQgZGF0YS4gVGhlc2Ugd2lsbCBiZSBsYWJlbGVkIGFzIENvdl9FZGdlcwpgYGB7cn0KIyBNYWtlIGZha2UgTElNT04gT2JqZWN0IHRvIFBhc3MgdG8gTmV0d29yayBJbmZlcmVuY2UgU3RlcHMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpvcmlnaW5hbF9kYXRhIDwtIHJlYWQuY3N2KGhlcmUoIkRhdGEiLCAiR0xWX1NpbURhdGEiLCAiRGF0YXNldF80IiwiR0xWX0Nvdl8wLjIwLmNzdiIpKQpvcmlnaW5hbF9kYXRhIDwtIG9yaWdpbmFsX2RhdGEgJT4lIGNvbHVtbl90b19yb3duYW1lcygiWCIpCgojIExvb3AgdGhyb3VnaCB0aGUgdGltZSBwb2ludHMsIGZpbHRlciBkYXRhLCBhbmQgcmVtb3ZlIG1ldGFkYXRhCmNvdW50c19saXN0IDwtIGxpc3QoKQpmb3IgKGkgaW4gMToxMCkgewogIGZpbHRlcmVkX2NvdW50cyA8LSBvcmlnaW5hbF9kYXRhICU+JSBmaWx0ZXIoVGltZSA9PSBpKQogIGNvdW50c19saXN0W1tpXV0gPC0gcm91bmQoZmlsdGVyZWRfY291bnRzWywgNjo1NV0pCn0KCiMgSW5pdGlhbGl6ZSBMX29ial9zaW0yCkxfb2JqX3NpbTIgPC0gTF9vYmoyCgojIEFzc2lnbiB0aGUgcHJvY2Vzc2VkIGRhdGEgdG8gdGhlIGNvcnJlc3BvbmRpbmcgc2xvdHMgaW4gTF9vYmpfc2ltMgpmb3IgKGkgaW4gMToxMCkgewogIExfb2JqX3NpbTJbWyJDb3JyZWN0ZWRfQ291bnRzX1RpbWUiXV1bW3Bhc3RlMCgiQ29ycmVjdGVkX0NvdW50c18iLCBpKV1dIDwtIGNvdW50c19saXN0W1tpXV0KfQoKCiMgTmV0d29yayBJbmZlcmVuY2UKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNldCBzZWVkCnNldC5zZWVkKDEyMzQ1KQpwc2VlZCA8LSBsaXN0KHJlcC5udW09NTAsIHNlZWQ9MTAwMTApCiMgU1BJRUMtRUFTSSBwZXIgdGltZQpMX29ial9zaW0zIDwtIExJTU9OX05ldEluZl9UaW1lKE9iaiA9IExfb2JqX3NpbTIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJnbGFzc28iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWwuY3JpdGVyaW9uID0gImJzdGFycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFtYmRhLm1pbi5yYXRpbyA9IDAuMDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsc2FyLnNlbGVjdD1UUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxzYXIucGFyYW1zPXBzZWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5sYW1iZGEgPSAyMDApCgoKIyBQcmludCBOZXR3b3JrcwpMX29ial9zaW01IDwtIExJTU9OX0VkZ2VzX05ldHdvcmtzKExfb2JqX3NpbTMsIHRocmVzaG9sZCA9IDAuMDAwMiwgdmVydGV4LnNpemUgPSAzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVydGV4LmxhYmVsLmNleCA9IDgsIHZlcnRleC5sYWJlbC5jb2xvciA9ICJibGFjayIpCgoKCmBgYAoKCiMjIyAyLjIuNCAtIE1lcmdlIE91dGNvbWVzCgpDYWxjdWxhdGUgdGhlIHNpbWlsYXIgZWRnZXMgYWNyb3NzIHRoZSB0aHJlZSBkYXRhIHR5cGVzCmBgYHtyfQojIExJTU9OIERhdGEgLSBFeHRyYWN0IEVkZ2VzCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIEluaXRpYWxpemUgYW4gZW1wdHkgbGlzdCB0byBzdG9yZSB0aGUgZGF0YSBmcmFtZXMKTF9FZGdlc19MaXN0IDwtIGxpc3QoKQoKIyBMb29wIHRocm91Z2ggdGhlIGVkZ2UgdGFibGVzIGFuZCBwcm9jZXNzIGVhY2ggb25lCmZvciAoaSBpbiAxOjEwKSB7CiAgZWRnZV90YWJsZSA8LSBhcy5kYXRhLmZyYW1lKExfb2JqNFtbIkVkZ2VfVGFibGUiXV1bW3Bhc3RlMCgiRWRnZV9UYWJsZV8iLCBpKV1dKQogICMgQ2hlY2sgaWYgdGhlIGVkZ2UgdGFibGUgaXMgZW1wdHkKICBpZiAobnJvdyhlZGdlX3RhYmxlKSA9PSAwKSB7CiAgICAjIENyZWF0ZSBhIGRhdGEgZnJhbWUgd2l0aCBOQSB2YWx1ZXMKICAgIGVkZ2VfdGFibGUgPC0gZGF0YS5mcmFtZShFZGdlX3dlaWdodCA9IE5BLCBUaW1lID0gaSkKICB9IGVsc2UgewogICAgIyBSZW5hbWUgdGhlIGNvbHVtbiBhbmQgYWRkIHRoZSBUaW1lIGNvbHVtbgogICAgZWRnZV90YWJsZSA8LSBlZGdlX3RhYmxlICU+JSBkcGx5cjo6cmVuYW1lKCJMX0VkZ2Vfd2VpZ2h0IiA9ICJFZGdlX3dlaWdodCIpCiAgICBlZGdlX3RhYmxlJFRpbWUgPC0gaQogIH0KICAjIEFwcGVuZCB0aGUgcHJvY2Vzc2VkIHRhYmxlIHRvIHRoZSBsaXN0CiAgTF9FZGdlc19MaXN0W1tpXV0gPC0gZWRnZV90YWJsZQp9CiMgQ29tYmluZSBhbGwgdGhlIHByb2Nlc3NlZCB0YWJsZXMgaW50byBhIHNpbmdsZSBkYXRhIGZyYW1lCkxfRWRnZXMgPC0gYmluZF9yb3dzKExfRWRnZXNfTGlzdCkKCgojIE9yaWdpbmFsIERhdGEgKyBDb3YgLSBFeHRyYWN0IGVkZ2VzCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIEluaXRpYWxpemUgYW4gZW1wdHkgbGlzdCB0byBzdG9yZSB0aGUgZGF0YSBmcmFtZXMKQ292X0VkZ2VzX0xpc3QgPC0gbGlzdCgpCgojIExvb3AgdGhyb3VnaCB0aGUgZWRnZSB0YWJsZXMgYW5kIHByb2Nlc3MgZWFjaCBvbmUKZm9yIChpIGluIDE6MTApIHsKICBlZGdlX3RhYmxlIDwtIGFzLmRhdGEuZnJhbWUoTF9vYmpfc2ltNVtbIkVkZ2VfVGFibGUiXV1bW3Bhc3RlMCgiRWRnZV9UYWJsZV8iLCBpKV1dKQogICMgQ2hlY2sgaWYgdGhlIGVkZ2UgdGFibGUgaXMgZW1wdHkKICBpZiAobnJvdyhlZGdlX3RhYmxlKSA9PSAwKSB7CiAgICAjIENyZWF0ZSBhIGRhdGEgZnJhbWUgd2l0aCBOQSB2YWx1ZXMKICAgIGVkZ2VfdGFibGUgPC0gZGF0YS5mcmFtZShFZGdlX3dlaWdodCA9IE5BLCBUaW1lID0gaSkKICB9IGVsc2UgewogICAgIyBSZW5hbWUgdGhlIGNvbHVtbiBhbmQgYWRkIHRoZSBUaW1lIGNvbHVtbgogICAgZWRnZV90YWJsZSA8LSBlZGdlX3RhYmxlICU+JSBkcGx5cjo6cmVuYW1lKCJDb3ZfRWRnZV93ZWlnaHQiPSAiRWRnZV93ZWlnaHQiKQogICAgZWRnZV90YWJsZSRUaW1lIDwtIGkKICB9CiAgIyBBcHBlbmQgdGhlIHByb2Nlc3NlZCB0YWJsZSB0byB0aGUgbGlzdAogIENvdl9FZGdlc19MaXN0W1tpXV0gPC0gZWRnZV90YWJsZQp9CiMgQ29tYmluZSBhbGwgdGhlIHByb2Nlc3NlZCB0YWJsZXMgaW50byBhIHNpbmdsZSBkYXRhIGZyYW1lCkNvdl9FZGdlcyA8LSBiaW5kX3Jvd3MoQ292X0VkZ2VzX0xpc3QpCgojIE9yaWdpbmFsIERhdGEgLSBFeHRyYWN0IEVkZ2VzCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgSW5pdGlhbGl6ZSBhbiBlbXB0eSBsaXN0IHRvIHN0b3JlIHRoZSBkYXRhIGZyYW1lcwpPX0VkZ2VzX0xpc3QgPC0gbGlzdCgpCgojIExvb3AgdGhyb3VnaCB0aGUgZWRnZSB0YWJsZXMgYW5kIHByb2Nlc3MgZWFjaCBvbmUKZm9yIChpIGluIDE6MTApIHsKICBlZGdlX3RhYmxlIDwtIGFzLmRhdGEuZnJhbWUoTF9vYmpfc2ltNFtbIkVkZ2VfVGFibGUiXV1bW3Bhc3RlMCgiRWRnZV9UYWJsZV8iLCBpKV1dKQogICMgQ2hlY2sgaWYgdGhlIGVkZ2UgdGFibGUgaXMgZW1wdHkKICBpZiAobnJvdyhlZGdlX3RhYmxlKSA9PSAwKSB7CiAgICAjIENyZWF0ZSBhIGRhdGEgZnJhbWUgd2l0aCBOQSB2YWx1ZXMKICAgIGVkZ2VfdGFibGUgPC0gZGF0YS5mcmFtZShFZGdlX3dlaWdodCA9IE5BLCBUaW1lID0gaSkKICB9IGVsc2UgewogICAgIyBSZW5hbWUgdGhlIGNvbHVtbiBhbmQgYWRkIHRoZSBUaW1lIGNvbHVtbgogICAgZWRnZV90YWJsZSA8LSBlZGdlX3RhYmxlICU+JSBkcGx5cjo6cmVuYW1lKCJPX0VkZ2Vfd2VpZ2h0Ij0gIkVkZ2Vfd2VpZ2h0IikKICAgIGVkZ2VfdGFibGUkVGltZSA8LSBpCiAgfQogICMgQXBwZW5kIHRoZSBwcm9jZXNzZWQgdGFibGUgdG8gdGhlIGxpc3QKICBPX0VkZ2VzX0xpc3RbW2ldXSA8LSBlZGdlX3RhYmxlCn0KIyBDb21iaW5lIGFsbCB0aGUgcHJvY2Vzc2VkIHRhYmxlcyBpbnRvIGEgc2luZ2xlIGRhdGEgZnJhbWUKT19FZGdlcyA8LSBiaW5kX3Jvd3MoT19FZGdlc19MaXN0KQoKCiMgQmluZCBkYXRhIHRvZ2V0aGVyCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCk1lcmdlZF9UMSA8LSBtZXJnZShMX0VkZ2VzLCBPX0VkZ2VzLCBieS55ID0gYygiU291cmNlIiwgIlNpbmsiLCAiVGltZSIpLCBhbGw9VFJVRSkKTWVyZ2VkX1QxIDwtIG1lcmdlKE1lcmdlZF9UMSwgQ292X0VkZ2VzLCBieS55ID0gYygiU291cmNlIiwgIlNpbmsiLCAiVGltZSIpLCBhbGw9VFJVRSkKCgojIENvbXBhcmlzb24gR3JhcGggRGF0YQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyBJZGVudGlmeSB0cnVlIGVkZ2VzCk1lcmdlZF9UMjAgPC0gTWVyZ2VkX1QxICU+JQogIGVkZ2VfcmVjb3ZlcnkoKSAKCiMgQWRkIENvbm5lY3RhbmNlIApNZXJnZWRfVDIwJENvbm5lY3RhbmNlIDwtIDAuMjAKYGBgCgoKCgojIyAyLjMgLSBDb25uZWN0YW5jZSA9IDAuNTAKCiMjIyAyLjMuMSAtIExJTU9OIG9uIENvdmFyaWF0ZSBJbmZsYXRlZCBEYXRhClJ1biB0aGUgTElNT04gY292YXJpYXRlIGNvcnJlY3Rpb24gYW5kIG5ldHdvcmsgaW5mZXJlbmNlIHN0ZXBzIG9uIENvdmFyaWF0ZSBpbmZsYXRlZCBkYXRhLiBUaGVzZSBlZGdlcyB3aWxsIGJlIHJlcXVpcmVkIGFzIExfRWRnZXMgIAoKCk1ha2UgTElNT04gT2JqZWN0CmBgYHtyfQpTaW1EYXRhIDwtIHJlYWQuY3N2KGhlcmUoIkRhdGEiLCAiR0xWX1NpbURhdGEiLCAiRGF0YXNldF80IiwiR0xWX0Nvdl8wLjUwLmNzdiIpKQpTaW1EYXRhIDwtIGNvbHVtbl90b19yb3duYW1lcyhTaW1EYXRhLCAiWCIpCgojIG1ldGFkYXRhCm1ldGFfZGF0YSA8LSBTaW1EYXRhWywxOjVdCm1ldGFfZGF0YSRUaW1lIDwtIGFzLm51bWVyaWMobWV0YV9kYXRhJFRpbWUpCm1ldGFfZGF0YSRCTUkgPC0gYXMubnVtZXJpYyhtZXRhX2RhdGEkQk1JKQptZXRhX2RhdGEkQWdlIDwtIGFzLm51bWVyaWMobWV0YV9kYXRhJEFnZSkKCiMgT3B0aW9uIHRvIHNob3J0ZW4gdG8gcnVuIGZhc3RlcgpsaW1vbl9tZXRhIDwtIG1ldGFfZGF0YSAjJT4lIGZpbHRlcihUaW1lICVpbiUgYygxLDIsMyw0LDUpKQoKIyBDb3VudCBkYXRhCmxpbW9uX2NvdW50cyA8LSBhcy5tYXRyaXgoU2ltRGF0YVssNjo1NV0pCgojIHNvcnQgcm93cwpsaW1vbl9jb3VudHMgPC0gbGltb25fY291bnRzW3Jvd25hbWVzKGxpbW9uX21ldGEpLF0KCiAgCkxfb2JqIDwtIExJTU9OX09iaihDb3VudHMgPSBsaW1vbl9jb3VudHMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBTYW1wbGVEYXRhID0gbGltb25fbWV0YSkKYGBgCgoKRGlzdHJpYnV0aW9uIEZpdHRpbmcgYW5kIGNoZWNrCmBgYHtyfQojIFNldCBzZWVkCnNldC5zZWVkKDEyMzQ1KQojIEZpdCB0aGUgZGlzdHJpYnV0aW9uL3JlbW92ZSBjb3ZhcmlhdGVzCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpMX29iajIgPC0gTElNT05fRGlzdHJGaXQoT2JqID0gTF9vYmosIAogICAgICAgICAgICAgICAgICAgICAgICAgICBUaW1lID0gIlRpbWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgU3ViamVjdCA9ICJJRCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBDb3ZhcmlhdGVzID0gYygiU2V4IiwgIkJNSSIsICJBZ2UiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWwgPSAiU2V4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzdHJpYnV0aW9uID0gIkxNTSIpCmBgYAoKCk5ldHdvcmtzIG9mIExJTU9OIERhdGEKYGBge3J9CiMgU2V0IHNlZWQKc2V0LnNlZWQoMTIzNDUpCnBzZWVkIDwtIGxpc3QocmVwLm51bT01MCwgc2VlZD0xMDAxMCkKCiMgU1BJRUMtRUFTSSBwZXIgdGltZQpMX29iajMgPC0gTElNT05fTmV0SW5mX1RpbWUoT2JqID0gTF9vYmoyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiZ2xhc3NvIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsLmNyaXRlcmlvbiA9ICJic3RhcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhbWJkYS5taW4ucmF0aW8gPSAwLjAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB1bHNhci5zZWxlY3Q9VFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsc2FyLnBhcmFtcz1wc2VlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBubGFtYmRhID0gMjAwKQoKCiMgUHJpbnQgTmV0d29ya3MKTF9vYmo0IDwtIExJTU9OX0VkZ2VzX05ldHdvcmtzKExfb2JqMywgdGhyZXNob2xkID0gMC4wMDAyLCB2ZXJ0ZXguc2l6ZSA9IDMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJ0ZXgubGFiZWwuY2V4ID0gOCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcnRleC5sYWJlbC5jb2xvciA9ICJibGFjayIpCmBgYAoKIyMjIDIuMy4yIC0gVHJ1ZSBOZXR3b3JrcyAtIG5vIENvdmFyaWF0ZXMgIAoKU1BJRUMtRUFTSSBOZXR3b3JrcyBvZiB0aGUgUmF3IERhdGEgd2l0aG91dCBjb3ZhcmlhdGVzLiBUaGVzZSB3aWxsIGJlIGxhYmVsZWQgYXMgT19FZGdlcyAoZm9yIG9yaWdpbmFsIGVkZ2VzKS4gV2UgcmVhZCBpbiB0aGUgZGF0YSBhbmQgdGhlbiBhc3NpZ24gaXQgdG8gYSBkb3duc3RyZWFtIExJTU9uIG9iamVjdCB0byBydW4gU1BJRUMtRUFTSSBhY3Jvc3MgYWxsIHRoZSB0aW1lIHBvaW50cyBidXQgYnlwYXNzaW5nIHRoZSBuZXR3b3JrIGluZmVyZW5jZSBzdGVwCmBgYHtyfQojIE1ha2UgZmFrZSBMSU1PTiBPYmplY3QgdG8gUGFzcyB0byBOZXR3b3JrIEluZmVyZW5jZSBTdGVwcwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCm9yaWdpbmFsX2RhdGEgPC0gcmVhZC5jc3YoaGVyZSgiRGF0YSIsICJHTFZfU2ltRGF0YSIsICJEYXRhc2V0XzQiLCJHTFZfMC41MC5jc3YiKSkKb3JpZ2luYWxfZGF0YSA8LSBvcmlnaW5hbF9kYXRhICU+JSBjb2x1bW5fdG9fcm93bmFtZXMoIlgiKQoKIyBMb29wIHRocm91Z2ggdGhlIHRpbWUgcG9pbnRzLCBmaWx0ZXIgZGF0YSwgYW5kIHJlbW92ZSBtZXRhZGF0YQpjb3VudHNfbGlzdCA8LSBsaXN0KCkKZm9yIChpIGluIDE6MTApIHsKICBmaWx0ZXJlZF9jb3VudHMgPC0gb3JpZ2luYWxfZGF0YSAlPiUgZmlsdGVyKFRpbWUgPT0gaSkKICBjb3VudHNfbGlzdFtbaV1dIDwtIHJvdW5kKGZpbHRlcmVkX2NvdW50c1ssIDY6NTVdKQp9CgojIEluaXRpYWxpemUgTF9vYmpfc2ltMgpMX29ial9zaW0yIDwtIExfb2JqMgoKIyBBc3NpZ24gdGhlIHByb2Nlc3NlZCBkYXRhIHRvIHRoZSBjb3JyZXNwb25kaW5nIHNsb3RzIGluIExfb2JqX3NpbTIKZm9yIChpIGluIDE6MTApIHsKICBMX29ial9zaW0yW1siQ29ycmVjdGVkX0NvdW50c19UaW1lIl1dW1twYXN0ZTAoIkNvcnJlY3RlZF9Db3VudHNfIiwgaSldXSA8LSBjb3VudHNfbGlzdFtbaV1dCn0KCgojIE5ldHdvcmsgSW5mZXJlbmNlCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTZXQgc2VlZApzZXQuc2VlZCgxMjM0NSkKcHNlZWQgPC0gbGlzdChyZXAubnVtPTUwLCBzZWVkPTEwMDEwKQojIFNQSUVDLUVBU0kgcGVyIHRpbWUKTF9vYmpfc2ltMyA8LSBMSU1PTl9OZXRJbmZfVGltZShPYmogPSBMX29ial9zaW0yLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiZ2xhc3NvIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsLmNyaXRlcmlvbiA9ICJic3RhcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhbWJkYS5taW4ucmF0aW8gPSAwLjAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB1bHNhci5zZWxlY3Q9VFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsc2FyLnBhcmFtcz1wc2VlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBubGFtYmRhID0gMjAwKQoKCiMgUHJpbnQgTmV0d29ya3MKTF9vYmpfc2ltNCA8LSBMSU1PTl9FZGdlc19OZXR3b3JrcyhMX29ial9zaW0zLCB0aHJlc2hvbGQgPSAwLjAwMDIsIHZlcnRleC5zaXplID0gMywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcnRleC5sYWJlbC5jZXggPSA4LCB2ZXJ0ZXgubGFiZWwuY29sb3IgPSAiYmxhY2siKQoKCgpgYGAKCgoKCiMjIyAyLjMuMyAtIFNQSUVDLUVBU0kgb24gQ292YXJpYXRlIERhdGEgIApTaW1pbGFyIHRvIHByb2Nlc3MgZm9yIG9yaWdpbmFsIG5ldHdvcmtzLCBleGNlcHQgbm93IHdlIHdpbGwgcnVuIGl0IG9uIGNvdmFyaWF0ZSBjb25mbGF0ZWQgZGF0YS4gVGhlc2Ugd2lsbCBiZSBsYWJlbGVkIGFzIENvdl9FZGdlcwpgYGB7cn0KIyBNYWtlIGZha2UgTElNT04gT2JqZWN0IHRvIFBhc3MgdG8gTmV0d29yayBJbmZlcmVuY2UgU3RlcHMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpvcmlnaW5hbF9kYXRhIDwtIHJlYWQuY3N2KGhlcmUoIkRhdGEiLCAiR0xWX1NpbURhdGEiLCAiRGF0YXNldF80IiwiR0xWX0Nvdl8wLjUwLmNzdiIpKQpvcmlnaW5hbF9kYXRhIDwtIG9yaWdpbmFsX2RhdGEgJT4lIGNvbHVtbl90b19yb3duYW1lcygiWCIpCgojIExvb3AgdGhyb3VnaCB0aGUgdGltZSBwb2ludHMsIGZpbHRlciBkYXRhLCBhbmQgcmVtb3ZlIG1ldGFkYXRhCmNvdW50c19saXN0IDwtIGxpc3QoKQpmb3IgKGkgaW4gMToxMCkgewogIGZpbHRlcmVkX2NvdW50cyA8LSBvcmlnaW5hbF9kYXRhICU+JSBmaWx0ZXIoVGltZSA9PSBpKQogIGNvdW50c19saXN0W1tpXV0gPC0gcm91bmQoZmlsdGVyZWRfY291bnRzWywgNjo1NV0pCn0KCiMgSW5pdGlhbGl6ZSBMX29ial9zaW0yCkxfb2JqX3NpbTIgPC0gTF9vYmoyCgojIEFzc2lnbiB0aGUgcHJvY2Vzc2VkIGRhdGEgdG8gdGhlIGNvcnJlc3BvbmRpbmcgc2xvdHMgaW4gTF9vYmpfc2ltMgpmb3IgKGkgaW4gMToxMCkgewogIExfb2JqX3NpbTJbWyJDb3JyZWN0ZWRfQ291bnRzX1RpbWUiXV1bW3Bhc3RlMCgiQ29ycmVjdGVkX0NvdW50c18iLCBpKV1dIDwtIGNvdW50c19saXN0W1tpXV0KfQoKCiMgTmV0d29yayBJbmZlcmVuY2UKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNldCBzZWVkCnNldC5zZWVkKDEyMzQ1KQpwc2VlZCA8LSBsaXN0KHJlcC5udW09NTAsIHNlZWQ9MTAwMTApCiMgU1BJRUMtRUFTSSBwZXIgdGltZQpMX29ial9zaW0zIDwtIExJTU9OX05ldEluZl9UaW1lKE9iaiA9IExfb2JqX3NpbTIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJnbGFzc28iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWwuY3JpdGVyaW9uID0gImJzdGFycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFtYmRhLm1pbi5yYXRpbyA9IDAuMDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsc2FyLnNlbGVjdD1UUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxzYXIucGFyYW1zPXBzZWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5sYW1iZGEgPSAyMDApCgoKIyBQcmludCBOZXR3b3JrcwpMX29ial9zaW01IDwtIExJTU9OX0VkZ2VzX05ldHdvcmtzKExfb2JqX3NpbTMsIHRocmVzaG9sZCA9IDAuMDAwMiwgdmVydGV4LnNpemUgPSAzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVydGV4LmxhYmVsLmNleCA9IDgsIHZlcnRleC5sYWJlbC5jb2xvciA9ICJibGFjayIpCgoKCmBgYAoKCiMjIyAyLjMuNCAtIE1lcmdlIE91dGNvbWVzCgpDYWxjdWxhdGUgdGhlIHNpbWlsYXIgZWRnZXMgYWNyb3NzIHRoZSB0aHJlZSBkYXRhIHR5cGVzCmBgYHtyfQojIExJTU9OIERhdGEgLSBFeHRyYWN0IEVkZ2VzCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIEluaXRpYWxpemUgYW4gZW1wdHkgbGlzdCB0byBzdG9yZSB0aGUgZGF0YSBmcmFtZXMKTF9FZGdlc19MaXN0IDwtIGxpc3QoKQoKIyBMb29wIHRocm91Z2ggdGhlIGVkZ2UgdGFibGVzIGFuZCBwcm9jZXNzIGVhY2ggb25lCmZvciAoaSBpbiAxOjEwKSB7CiAgZWRnZV90YWJsZSA8LSBhcy5kYXRhLmZyYW1lKExfb2JqNFtbIkVkZ2VfVGFibGUiXV1bW3Bhc3RlMCgiRWRnZV9UYWJsZV8iLCBpKV1dKQogICMgQ2hlY2sgaWYgdGhlIGVkZ2UgdGFibGUgaXMgZW1wdHkKICBpZiAobnJvdyhlZGdlX3RhYmxlKSA9PSAwKSB7CiAgICAjIENyZWF0ZSBhIGRhdGEgZnJhbWUgd2l0aCBOQSB2YWx1ZXMKICAgIGVkZ2VfdGFibGUgPC0gZGF0YS5mcmFtZShFZGdlX3dlaWdodCA9IE5BLCBUaW1lID0gaSkKICB9IGVsc2UgewogICAgIyBSZW5hbWUgdGhlIGNvbHVtbiBhbmQgYWRkIHRoZSBUaW1lIGNvbHVtbgogICAgZWRnZV90YWJsZSA8LSBlZGdlX3RhYmxlICU+JSBkcGx5cjo6cmVuYW1lKCJMX0VkZ2Vfd2VpZ2h0Ij0gIkVkZ2Vfd2VpZ2h0IikKICAgIGVkZ2VfdGFibGUkVGltZSA8LSBpCiAgfQogICMgQXBwZW5kIHRoZSBwcm9jZXNzZWQgdGFibGUgdG8gdGhlIGxpc3QKICBMX0VkZ2VzX0xpc3RbW2ldXSA8LSBlZGdlX3RhYmxlCn0KIyBDb21iaW5lIGFsbCB0aGUgcHJvY2Vzc2VkIHRhYmxlcyBpbnRvIGEgc2luZ2xlIGRhdGEgZnJhbWUKTF9FZGdlcyA8LSBiaW5kX3Jvd3MoTF9FZGdlc19MaXN0KQoKCiMgT3JpZ2luYWwgRGF0YSArIENvdiAtIEV4dHJhY3QgZWRnZXMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCiMgSW5pdGlhbGl6ZSBhbiBlbXB0eSBsaXN0IHRvIHN0b3JlIHRoZSBkYXRhIGZyYW1lcwpDb3ZfRWRnZXNfTGlzdCA8LSBsaXN0KCkKCiMgTG9vcCB0aHJvdWdoIHRoZSBlZGdlIHRhYmxlcyBhbmQgcHJvY2VzcyBlYWNoIG9uZQpmb3IgKGkgaW4gMToxMCkgewogIGVkZ2VfdGFibGUgPC0gYXMuZGF0YS5mcmFtZShMX29ial9zaW01W1siRWRnZV9UYWJsZSJdXVtbcGFzdGUwKCJFZGdlX1RhYmxlXyIsIGkpXV0pCiAgIyBDaGVjayBpZiB0aGUgZWRnZSB0YWJsZSBpcyBlbXB0eQogIGlmIChucm93KGVkZ2VfdGFibGUpID09IDApIHsKICAgICMgQ3JlYXRlIGEgZGF0YSBmcmFtZSB3aXRoIE5BIHZhbHVlcwogICAgZWRnZV90YWJsZSA8LSBkYXRhLmZyYW1lKEVkZ2Vfd2VpZ2h0ID0gTkEsIFRpbWUgPSBpKQogIH0gZWxzZSB7CiAgICAjIFJlbmFtZSB0aGUgY29sdW1uIGFuZCBhZGQgdGhlIFRpbWUgY29sdW1uCiAgICBlZGdlX3RhYmxlIDwtIGVkZ2VfdGFibGUgJT4lIGRwbHlyOjpyZW5hbWUoIkNvdl9FZGdlX3dlaWdodCI9ICJFZGdlX3dlaWdodCIpCiAgICBlZGdlX3RhYmxlJFRpbWUgPC0gaQogIH0KICAjIEFwcGVuZCB0aGUgcHJvY2Vzc2VkIHRhYmxlIHRvIHRoZSBsaXN0CiAgQ292X0VkZ2VzX0xpc3RbW2ldXSA8LSBlZGdlX3RhYmxlCn0KIyBDb21iaW5lIGFsbCB0aGUgcHJvY2Vzc2VkIHRhYmxlcyBpbnRvIGEgc2luZ2xlIGRhdGEgZnJhbWUKQ292X0VkZ2VzIDwtIGJpbmRfcm93cyhDb3ZfRWRnZXNfTGlzdCkKCiMgT3JpZ2luYWwgRGF0YSAtIEV4dHJhY3QgRWRnZXMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBJbml0aWFsaXplIGFuIGVtcHR5IGxpc3QgdG8gc3RvcmUgdGhlIGRhdGEgZnJhbWVzCk9fRWRnZXNfTGlzdCA8LSBsaXN0KCkKCiMgTG9vcCB0aHJvdWdoIHRoZSBlZGdlIHRhYmxlcyBhbmQgcHJvY2VzcyBlYWNoIG9uZQpmb3IgKGkgaW4gMToxMCkgewogIGVkZ2VfdGFibGUgPC0gYXMuZGF0YS5mcmFtZShMX29ial9zaW00W1siRWRnZV9UYWJsZSJdXVtbcGFzdGUwKCJFZGdlX1RhYmxlXyIsIGkpXV0pCiAgIyBDaGVjayBpZiB0aGUgZWRnZSB0YWJsZSBpcyBlbXB0eQogIGlmIChucm93KGVkZ2VfdGFibGUpID09IDApIHsKICAgICMgQ3JlYXRlIGEgZGF0YSBmcmFtZSB3aXRoIE5BIHZhbHVlcwogICAgZWRnZV90YWJsZSA8LSBkYXRhLmZyYW1lKEVkZ2Vfd2VpZ2h0ID0gTkEsIFRpbWUgPSBpKQogIH0gZWxzZSB7CiAgICAjIFJlbmFtZSB0aGUgY29sdW1uIGFuZCBhZGQgdGhlIFRpbWUgY29sdW1uCiAgICBlZGdlX3RhYmxlIDwtIGVkZ2VfdGFibGUgJT4lIGRwbHlyOjpyZW5hbWUoIk9fRWRnZV93ZWlnaHQiPSAiRWRnZV93ZWlnaHQiKQogICAgZWRnZV90YWJsZSRUaW1lIDwtIGkKICB9CiAgIyBBcHBlbmQgdGhlIHByb2Nlc3NlZCB0YWJsZSB0byB0aGUgbGlzdAogIE9fRWRnZXNfTGlzdFtbaV1dIDwtIGVkZ2VfdGFibGUKfQojIENvbWJpbmUgYWxsIHRoZSBwcm9jZXNzZWQgdGFibGVzIGludG8gYSBzaW5nbGUgZGF0YSBmcmFtZQpPX0VkZ2VzIDwtIGJpbmRfcm93cyhPX0VkZ2VzX0xpc3QpCgojIEJpbmQgZGF0YSB0b2dldGhlcgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpNZXJnZWRfVDEgPC0gbWVyZ2UoTF9FZGdlcywgT19FZGdlcywgYnkueSA9IGMoIlNvdXJjZSIsICJTaW5rIiwgIlRpbWUiKSwgYWxsPVRSVUUpCk1lcmdlZF9UMSA8LSBtZXJnZShNZXJnZWRfVDEsIENvdl9FZGdlcywgYnkueSA9IGMoIlNvdXJjZSIsICJTaW5rIiwgIlRpbWUiKSwgYWxsPVRSVUUpCgoKIyBDb21wYXJpc29uIEdyYXBoIERhdGEKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCiMgSWRlbnRpZnkgdHJ1ZSBlZGdlcwpNZXJnZWRfVDUwIDwtIE1lcmdlZF9UMSAlPiUKICBlZGdlX3JlY292ZXJ5KCkgCgojIEFkZCBDb25uZWN0YW5jZSAKTWVyZ2VkX1Q1MCRDb25uZWN0YW5jZSA8LSAwLjUwCmBgYAoKCgojIyAyLjQgLSBDb25uZWN0YW5jZSA9IDAuNzUKCiMjIyAyLjQuMSAtIExJTU9OIG9uIENvdmFyaWF0ZSBJbmZsYXRlZCBEYXRhClJ1biB0aGUgTElNT04gY292YXJpYXRlIGNvcnJlY3Rpb24gYW5kIG5ldHdvcmsgaW5mZXJlbmNlIHN0ZXBzIG9uIENvdmFyaWF0ZSBpbmZsYXRlZCBkYXRhLiBUaGVzZSBlZGdlcyB3aWxsIGJlIHJlcXVpcmVkIGFzIExfRWRnZXMgIAoKCk1ha2UgTElNT04gT2JqZWN0CmBgYHtyfQpTaW1EYXRhIDwtIHJlYWQuY3N2KGhlcmUoIkRhdGEiLCAiR0xWX1NpbURhdGEiLCAiRGF0YXNldF80IiwiR0xWX0Nvdl8wLjc1LmNzdiIpKQpTaW1EYXRhIDwtIGNvbHVtbl90b19yb3duYW1lcyhTaW1EYXRhLCAiWCIpCgojIG1ldGFkYXRhCm1ldGFfZGF0YSA8LSBTaW1EYXRhWywxOjVdCm1ldGFfZGF0YSRUaW1lIDwtIGFzLm51bWVyaWMobWV0YV9kYXRhJFRpbWUpCm1ldGFfZGF0YSRCTUkgPC0gYXMubnVtZXJpYyhtZXRhX2RhdGEkQk1JKQptZXRhX2RhdGEkQWdlIDwtIGFzLm51bWVyaWMobWV0YV9kYXRhJEFnZSkKCiMgT3B0aW9uIHRvIHNob3J0ZW4gdG8gcnVuIGZhc3RlcgpsaW1vbl9tZXRhIDwtIG1ldGFfZGF0YSAjJT4lIGZpbHRlcihUaW1lICVpbiUgYygxLDIsMyw0LDUpKQoKIyBDb3VudCBkYXRhCmxpbW9uX2NvdW50cyA8LSBhcy5tYXRyaXgoU2ltRGF0YVssNjo1NV0pCgojIHNvcnQgcm93cwpsaW1vbl9jb3VudHMgPC0gbGltb25fY291bnRzW3Jvd25hbWVzKGxpbW9uX21ldGEpLF0KCiAgCkxfb2JqIDwtIExJTU9OX09iaihDb3VudHMgPSBsaW1vbl9jb3VudHMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBTYW1wbGVEYXRhID0gbGltb25fbWV0YSkKYGBgCgoKRGlzdHJpYnV0aW9uIEZpdHRpbmcgYW5kIGNoZWNrCmBgYHtyfQojIFNldCBzZWVkCnNldC5zZWVkKDEyMzQ1KQojIEZpdCB0aGUgZGlzdHJpYnV0aW9uL3JlbW92ZSBjb3ZhcmlhdGVzCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpMX29iajIgPC0gTElNT05fRGlzdHJGaXQoT2JqID0gTF9vYmosIAogICAgICAgICAgICAgICAgICAgICAgICAgICBUaW1lID0gIlRpbWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgU3ViamVjdCA9ICJJRCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBDb3ZhcmlhdGVzID0gYygiU2V4IiwgIkJNSSIsICJBZ2UiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWwgPSAiU2V4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzdHJpYnV0aW9uID0gIkxNTSIpCmBgYAoKCk5ldHdvcmtzIG9mIExJTU9OIERhdGEKYGBge3J9CiMgU2V0IHNlZWQKc2V0LnNlZWQoMTIzNDUpCnBzZWVkIDwtIGxpc3QocmVwLm51bT01MCwgc2VlZD0xMDAxMCkKCiMgU1BJRUMtRUFTSSBwZXIgdGltZQpMX29iajMgPC0gTElNT05fTmV0SW5mX1RpbWUoT2JqID0gTF9vYmoyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiZ2xhc3NvIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsLmNyaXRlcmlvbiA9ICJic3RhcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhbWJkYS5taW4ucmF0aW8gPSAwLjAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB1bHNhci5zZWxlY3Q9VFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsc2FyLnBhcmFtcz1wc2VlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBubGFtYmRhID0gMjAwKQoKCiMgUHJpbnQgTmV0d29ya3MKTF9vYmo0IDwtIExJTU9OX0VkZ2VzX05ldHdvcmtzKExfb2JqMywgdGhyZXNob2xkID0gMC4wMDAyLCB2ZXJ0ZXguc2l6ZSA9IDMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJ0ZXgubGFiZWwuY2V4ID0gOCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcnRleC5sYWJlbC5jb2xvciA9ICJibGFjayIpCmBgYAoKIyMjIDIuNC4yIC0gVHJ1ZSBOZXR3b3JrcyAtIG5vIENvdmFyaWF0ZXMgIAoKU1BJRUMtRUFTSSBOZXR3b3JrcyBvZiB0aGUgUmF3IERhdGEgd2l0aG91dCBjb3ZhcmlhdGVzLiBUaGVzZSB3aWxsIGJlIGxhYmVsZWQgYXMgT19FZGdlcyAoZm9yIG9yaWdpbmFsIGVkZ2VzKS4gV2UgcmVhZCBpbiB0aGUgZGF0YSBhbmQgdGhlbiBhc3NpZ24gaXQgdG8gYSBkb3duc3RyZWFtIExJTU9uIG9iamVjdCB0byBydW4gU1BJRUMtRUFTSSBhY3Jvc3MgYWxsIHRoZSB0aW1lcG9pbnRzIGJ1dCBieXBhc3NpbmcgdGhlIG5ldHdvcmsgaW5mZXJlbmNlIHN0ZXAKYGBge3J9CiMgTWFrZSBmYWtlIExJTU9OIE9iamVjdCB0byBQYXNzIHRvIE5ldHdvcmsgSW5mZXJlbmNlIFN0ZXBzCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKb3JpZ2luYWxfZGF0YSA8LSByZWFkLmNzdihoZXJlKCJEYXRhIiwgIkdMVl9TaW1EYXRhIiwgIkRhdGFzZXRfNCIsIkdMVl8wLjc1LmNzdiIpKQpvcmlnaW5hbF9kYXRhIDwtIG9yaWdpbmFsX2RhdGEgJT4lIGNvbHVtbl90b19yb3duYW1lcygiWCIpCgojIExvb3AgdGhyb3VnaCB0aGUgdGltZSBwb2ludHMsIGZpbHRlciBkYXRhLCBhbmQgcmVtb3ZlIG1ldGFkYXRhCmNvdW50c19saXN0IDwtIGxpc3QoKQpmb3IgKGkgaW4gMToxMCkgewogIGZpbHRlcmVkX2NvdW50cyA8LSBvcmlnaW5hbF9kYXRhICU+JSBmaWx0ZXIoVGltZSA9PSBpKQogIGNvdW50c19saXN0W1tpXV0gPC0gcm91bmQoZmlsdGVyZWRfY291bnRzWywgNjo1NV0pCn0KCiMgSW5pdGlhbGl6ZSBMX29ial9zaW0yCkxfb2JqX3NpbTIgPC0gTF9vYmoyCgojIEFzc2lnbiB0aGUgcHJvY2Vzc2VkIGRhdGEgdG8gdGhlIGNvcnJlc3BvbmRpbmcgc2xvdHMgaW4gTF9vYmpfc2ltMgpmb3IgKGkgaW4gMToxMCkgewogIExfb2JqX3NpbTJbWyJDb3JyZWN0ZWRfQ291bnRzX1RpbWUiXV1bW3Bhc3RlMCgiQ29ycmVjdGVkX0NvdW50c18iLCBpKV1dIDwtIGNvdW50c19saXN0W1tpXV0KfQoKCiMgTmV0d29yayBJbmZlcmVuY2UKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNldCBzZWVkCnNldC5zZWVkKDEyMzQ1KQpwc2VlZCA8LSBsaXN0KHJlcC5udW09NTAsIHNlZWQ9MTAwMTApCiMgU1BJRUMtRUFTSSBwZXIgdGltZQpMX29ial9zaW0zIDwtIExJTU9OX05ldEluZl9UaW1lKE9iaiA9IExfb2JqX3NpbTIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJnbGFzc28iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWwuY3JpdGVyaW9uID0gImJzdGFycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFtYmRhLm1pbi5yYXRpbyA9IDAuMDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsc2FyLnNlbGVjdD1UUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxzYXIucGFyYW1zPXBzZWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5sYW1iZGEgPSAyMDApCgoKIyBQcmludCBOZXR3b3JrcwpMX29ial9zaW00IDwtIExJTU9OX0VkZ2VzX05ldHdvcmtzKExfb2JqX3NpbTMsIHRocmVzaG9sZCA9IDAuMDAwMiwgdmVydGV4LnNpemUgPSAzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVydGV4LmxhYmVsLmNleCA9IDgsIHZlcnRleC5sYWJlbC5jb2xvciA9ICJibGFjayIpCgoKCmBgYAoKCgoKIyMjIDIuNC4zIC0gU1BJRUMtRUFTSSBvbiBDb3ZhcmlhdGUgRGF0YSAgClNpbWlsYXIgdG8gcHJvY2VzcyBmb3Igb3JpZ2luYWwgbmV0d29ya3MsIGV4Y2VwdCBub3cgd2Ugd2lsbCBydW4gaXQgb24gY292YXJpYXRlIGNvbmZsYXRlZCBkYXRhLiBUaGVzZSB3aWxsIGJlIGxhYmVsZWQgYXMgQ292X0VkZ2VzCmBgYHtyfQojIE1ha2UgZmFrZSBMSU1PTiBPYmplY3QgdG8gUGFzcyB0byBOZXR3b3JrIEluZmVyZW5jZSBTdGVwcwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCm9yaWdpbmFsX2RhdGEgPC0gcmVhZC5jc3YoaGVyZSgiRGF0YSIsICJHTFZfU2ltRGF0YSIsICJEYXRhc2V0XzQiLCJHTFZfQ292XzAuNzUuY3N2IikpCm9yaWdpbmFsX2RhdGEgPC0gb3JpZ2luYWxfZGF0YSAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCJYIikKCiMgTG9vcCB0aHJvdWdoIHRoZSB0aW1lIHBvaW50cywgZmlsdGVyIGRhdGEsIGFuZCByZW1vdmUgbWV0YWRhdGEKY291bnRzX2xpc3QgPC0gbGlzdCgpCmZvciAoaSBpbiAxOjEwKSB7CiAgZmlsdGVyZWRfY291bnRzIDwtIG9yaWdpbmFsX2RhdGEgJT4lIGZpbHRlcihUaW1lID09IGkpCiAgY291bnRzX2xpc3RbW2ldXSA8LSByb3VuZChmaWx0ZXJlZF9jb3VudHNbLCA2OjU1XSkKfQoKIyBJbml0aWFsaXplIExfb2JqX3NpbTIKTF9vYmpfc2ltMiA8LSBMX29iajIKCiMgQXNzaWduIHRoZSBwcm9jZXNzZWQgZGF0YSB0byB0aGUgY29ycmVzcG9uZGluZyBzbG90cyBpbiBMX29ial9zaW0yCmZvciAoaSBpbiAxOjEwKSB7CiAgTF9vYmpfc2ltMltbIkNvcnJlY3RlZF9Db3VudHNfVGltZSJdXVtbcGFzdGUwKCJDb3JyZWN0ZWRfQ291bnRzXyIsIGkpXV0gPC0gY291bnRzX2xpc3RbW2ldXQp9CgoKIyBOZXR3b3JrIEluZmVyZW5jZQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU2V0IHNlZWQKc2V0LnNlZWQoMTIzNDUpCnBzZWVkIDwtIGxpc3QocmVwLm51bT01MCwgc2VlZD0xMDAxMCkKIyBTUElFQy1FQVNJIHBlciB0aW1lCkxfb2JqX3NpbTMgPC0gTElNT05fTmV0SW5mX1RpbWUoT2JqID0gTF9vYmpfc2ltMiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImdsYXNzbyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbC5jcml0ZXJpb24gPSAiYnN0YXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYW1iZGEubWluLnJhdGlvID0gMC4wMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxzYXIuc2VsZWN0PVRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB1bHNhci5wYXJhbXM9cHNlZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmxhbWJkYSA9IDIwMCkKCgojIFByaW50IE5ldHdvcmtzCkxfb2JqX3NpbTUgPC0gTElNT05fRWRnZXNfTmV0d29ya3MoTF9vYmpfc2ltMywgdGhyZXNob2xkID0gMC4wMDAyLCB2ZXJ0ZXguc2l6ZSA9IDMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJ0ZXgubGFiZWwuY2V4ID0gOCwgdmVydGV4LmxhYmVsLmNvbG9yID0gImJsYWNrIikKCgoKYGBgCgoKIyMjIDIuNC40IC0gTWVyZ2UgT3V0Y29tZXMKQ2FsY3VsYXRlIHRoZSBzaW1pbGFyIGVkZ2VzIGFjcm9zcyB0aGUgdGhyZWUgZGF0YSB0eXBlcwpgYGB7cn0KIyBMSU1PTiBEYXRhIC0gRXh0cmFjdCBFZGdlcwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyBJbml0aWFsaXplIGFuIGVtcHR5IGxpc3QgdG8gc3RvcmUgdGhlIGRhdGEgZnJhbWVzCkxfRWRnZXNfTGlzdCA8LSBsaXN0KCkKCiMgTG9vcCB0aHJvdWdoIHRoZSBlZGdlIHRhYmxlcyBhbmQgcHJvY2VzcyBlYWNoIG9uZQpmb3IgKGkgaW4gMToxMCkgewogIGVkZ2VfdGFibGUgPC0gYXMuZGF0YS5mcmFtZShMX29iajRbWyJFZGdlX1RhYmxlIl1dW1twYXN0ZTAoIkVkZ2VfVGFibGVfIiwgaSldXSkKICAjIENoZWNrIGlmIHRoZSBlZGdlIHRhYmxlIGlzIGVtcHR5CiAgaWYgKG5yb3coZWRnZV90YWJsZSkgPT0gMCkgewogICAgIyBDcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggTkEgdmFsdWVzCiAgICBlZGdlX3RhYmxlIDwtIGRhdGEuZnJhbWUoRWRnZV93ZWlnaHQgPSBOQSwgVGltZSA9IGkpCiAgfSBlbHNlIHsKICAgICMgUmVuYW1lIHRoZSBjb2x1bW4gYW5kIGFkZCB0aGUgVGltZSBjb2x1bW4KICAgIGVkZ2VfdGFibGUgPC0gZWRnZV90YWJsZSAlPiUgZHBseXI6OnJlbmFtZSgiTF9FZGdlX3dlaWdodCI9ICJFZGdlX3dlaWdodCIpCiAgICBlZGdlX3RhYmxlJFRpbWUgPC0gaQogIH0KICAjIEFwcGVuZCB0aGUgcHJvY2Vzc2VkIHRhYmxlIHRvIHRoZSBsaXN0CiAgTF9FZGdlc19MaXN0W1tpXV0gPC0gZWRnZV90YWJsZQp9CiMgQ29tYmluZSBhbGwgdGhlIHByb2Nlc3NlZCB0YWJsZXMgaW50byBhIHNpbmdsZSBkYXRhIGZyYW1lCkxfRWRnZXMgPC0gYmluZF9yb3dzKExfRWRnZXNfTGlzdCkKCiMgT3JpZ2luYWwgRGF0YSArIENvdiAtIEV4dHJhY3QgZWRnZXMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCiMgSW5pdGlhbGl6ZSBhbiBlbXB0eSBsaXN0IHRvIHN0b3JlIHRoZSBkYXRhIGZyYW1lcwpDb3ZfRWRnZXNfTGlzdCA8LSBsaXN0KCkKCiMgTG9vcCB0aHJvdWdoIHRoZSBlZGdlIHRhYmxlcyBhbmQgcHJvY2VzcyBlYWNoIG9uZQpmb3IgKGkgaW4gMToxMCkgewogIGVkZ2VfdGFibGUgPC0gYXMuZGF0YS5mcmFtZShMX29ial9zaW01W1siRWRnZV9UYWJsZSJdXVtbcGFzdGUwKCJFZGdlX1RhYmxlXyIsIGkpXV0pCiAgIyBDaGVjayBpZiB0aGUgZWRnZSB0YWJsZSBpcyBlbXB0eQogIGlmIChucm93KGVkZ2VfdGFibGUpID09IDApIHsKICAgICMgQ3JlYXRlIGEgZGF0YSBmcmFtZSB3aXRoIE5BIHZhbHVlcwogICAgZWRnZV90YWJsZSA8LSBkYXRhLmZyYW1lKEVkZ2Vfd2VpZ2h0ID0gTkEsIFRpbWUgPSBpKQogIH0gZWxzZSB7CiAgICAjIFJlbmFtZSB0aGUgY29sdW1uIGFuZCBhZGQgdGhlIFRpbWUgY29sdW1uCiAgICBlZGdlX3RhYmxlIDwtIGVkZ2VfdGFibGUgJT4lIGRwbHlyOjpyZW5hbWUoIkNvdl9FZGdlX3dlaWdodCI9ICJFZGdlX3dlaWdodCIpCiAgICBlZGdlX3RhYmxlJFRpbWUgPC0gaQogIH0KICAjIEFwcGVuZCB0aGUgcHJvY2Vzc2VkIHRhYmxlIHRvIHRoZSBsaXN0CiAgQ292X0VkZ2VzX0xpc3RbW2ldXSA8LSBlZGdlX3RhYmxlCn0KIyBDb21iaW5lIGFsbCB0aGUgcHJvY2Vzc2VkIHRhYmxlcyBpbnRvIGEgc2luZ2xlIGRhdGEgZnJhbWUKQ292X0VkZ2VzIDwtIGJpbmRfcm93cyhDb3ZfRWRnZXNfTGlzdCkKCiMgT3JpZ2luYWwgRGF0YSAtIEV4dHJhY3QgRWRnZXMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBJbml0aWFsaXplIGFuIGVtcHR5IGxpc3QgdG8gc3RvcmUgdGhlIGRhdGEgZnJhbWVzCk9fRWRnZXNfTGlzdCA8LSBsaXN0KCkKCiMgTG9vcCB0aHJvdWdoIHRoZSBlZGdlIHRhYmxlcyBhbmQgcHJvY2VzcyBlYWNoIG9uZQpmb3IgKGkgaW4gMToxMCkgewogIGVkZ2VfdGFibGUgPC0gYXMuZGF0YS5mcmFtZShMX29ial9zaW00W1siRWRnZV9UYWJsZSJdXVtbcGFzdGUwKCJFZGdlX1RhYmxlXyIsIGkpXV0pCiAgIyBDaGVjayBpZiB0aGUgZWRnZSB0YWJsZSBpcyBlbXB0eQogIGlmIChucm93KGVkZ2VfdGFibGUpID09IDApIHsKICAgICMgQ3JlYXRlIGEgZGF0YSBmcmFtZSB3aXRoIE5BIHZhbHVlcwogICAgZWRnZV90YWJsZSA8LSBkYXRhLmZyYW1lKEVkZ2Vfd2VpZ2h0ID0gTkEsIFRpbWUgPSBpKQogIH0gZWxzZSB7CiAgICAjIFJlbmFtZSB0aGUgY29sdW1uIGFuZCBhZGQgdGhlIFRpbWUgY29sdW1uCiAgICBlZGdlX3RhYmxlIDwtIGVkZ2VfdGFibGUgJT4lIGRwbHlyOjpyZW5hbWUoIk9fRWRnZV93ZWlnaHQiPSAiRWRnZV93ZWlnaHQiKQogICAgZWRnZV90YWJsZSRUaW1lIDwtIGkKICB9CiAgIyBBcHBlbmQgdGhlIHByb2Nlc3NlZCB0YWJsZSB0byB0aGUgbGlzdAogIE9fRWRnZXNfTGlzdFtbaV1dIDwtIGVkZ2VfdGFibGUKfQojIENvbWJpbmUgYWxsIHRoZSBwcm9jZXNzZWQgdGFibGVzIGludG8gYSBzaW5nbGUgZGF0YSBmcmFtZQpPX0VkZ2VzIDwtIGJpbmRfcm93cyhPX0VkZ2VzX0xpc3QpCgojIEJpbmQgZGF0YSB0b2dldGhlcgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpNZXJnZWRfVDEgPC0gbWVyZ2UoTF9FZGdlcywgT19FZGdlcywgYnkueSA9IGMoIlNvdXJjZSIsICJTaW5rIiwgIlRpbWUiKSwgYWxsPVRSVUUpCk1lcmdlZF9UMSA8LSBtZXJnZShNZXJnZWRfVDEsIENvdl9FZGdlcywgYnkueSA9IGMoIlNvdXJjZSIsICJTaW5rIiwgIlRpbWUiKSwgYWxsPVRSVUUpCgoKIyBDb21wYXJpc29uIEdyYXBoIERhdGEKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCiMgSWRlbnRpZnkgdHJ1ZSBlZGdlcwpNZXJnZWRfVDc1IDwtIE1lcmdlZF9UMSAlPiUKICBlZGdlX3JlY292ZXJ5KCkgCgojIEFkZCBDb25uZWN0YW5jZSAKTWVyZ2VkX1Q3NSRDb25uZWN0YW5jZSA8LSAwLjc1CmBgYAoKCgojIyAyLjUgLSBDb25uZWN0YW5jZSA9IDAuOTAKCiMjIyAyLjUuMSAtIExJTU9OIG9uIENvdmFyaWF0ZSBJbmZsYXRlZCBEYXRhClJ1biB0aGUgTElNT04gY292YXJpYXRlIGNvcnJlY3Rpb24gYW5kIG5ldHdvcmsgaW5mZXJlbmNlIHN0ZXBzIG9uIENvdmFyaWF0ZSBpbmZsYXRlZCBkYXRhLiBUaGVzZSBlZGdlcyB3aWxsIGJlIHJlcXVpcmVkIGFzIExfRWRnZXMgIAoKCk1ha2UgTElNT04gT2JqZWN0CmBgYHtyfQpTaW1EYXRhIDwtIHJlYWQuY3N2KGhlcmUoIkRhdGEiLCAiR0xWX1NpbURhdGEiLCAiRGF0YXNldF80IiwiR0xWX0Nvdl8wLjkwLmNzdiIpKQpTaW1EYXRhIDwtIGNvbHVtbl90b19yb3duYW1lcyhTaW1EYXRhLCAiWCIpCgojIG1ldGFkYXRhCm1ldGFfZGF0YSA8LSBTaW1EYXRhWywxOjVdCm1ldGFfZGF0YSRUaW1lIDwtIGFzLm51bWVyaWMobWV0YV9kYXRhJFRpbWUpCm1ldGFfZGF0YSRCTUkgPC0gYXMubnVtZXJpYyhtZXRhX2RhdGEkQk1JKQptZXRhX2RhdGEkQWdlIDwtIGFzLm51bWVyaWMobWV0YV9kYXRhJEFnZSkKCiMgT3B0aW9uIHRvIHNob3J0ZW4gdG8gcnVuIGZhc3RlcgpsaW1vbl9tZXRhIDwtIG1ldGFfZGF0YSAjJT4lIGZpbHRlcihUaW1lICVpbiUgYygxLDIsMyw0LDUpKQoKIyBDb3VudCBkYXRhCmxpbW9uX2NvdW50cyA8LSBhcy5tYXRyaXgoU2ltRGF0YVssNjo1NV0pCgojIHNvcnQgcm93cwpsaW1vbl9jb3VudHMgPC0gbGltb25fY291bnRzW3Jvd25hbWVzKGxpbW9uX21ldGEpLF0KCiAgCkxfb2JqIDwtIExJTU9OX09iaihDb3VudHMgPSBsaW1vbl9jb3VudHMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBTYW1wbGVEYXRhID0gbGltb25fbWV0YSkKYGBgCgoKRGlzdHJpYnV0aW9uIEZpdHRpbmcgYW5kIGNoZWNrCmBgYHtyfQojIFNldCBzZWVkCnNldC5zZWVkKDEyMzQ1KQojIEZpdCB0aGUgZGlzdHJpYnV0aW9uL3JlbW92ZSBjb3ZhcmlhdGVzCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpMX29iajIgPC0gTElNT05fRGlzdHJGaXQoT2JqID0gTF9vYmosIAogICAgICAgICAgICAgICAgICAgICAgICAgICBUaW1lID0gIlRpbWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgU3ViamVjdCA9ICJJRCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBDb3ZhcmlhdGVzID0gYygiU2V4IiwgIkJNSSIsICJBZ2UiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWwgPSAiU2V4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzdHJpYnV0aW9uID0gIkxNTSIpCmBgYAoKCgoKTmV0d29ya3Mgb2YgTElNT04gRGF0YQpgYGB7cn0KIyBTZXQgc2VlZApzZXQuc2VlZCgxMjM0NSkKcHNlZWQgPC0gbGlzdChyZXAubnVtPTUwLCBzZWVkPTEwMDEwKQoKIyBTUElFQy1FQVNJIHBlciB0aW1lCkxfb2JqMyA8LSBMSU1PTl9OZXRJbmZfVGltZShPYmogPSBMX29iajIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJnbGFzc28iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWwuY3JpdGVyaW9uID0gImJzdGFycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFtYmRhLm1pbi5yYXRpbyA9IDAuMDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsc2FyLnNlbGVjdD1UUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxzYXIucGFyYW1zPXBzZWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5sYW1iZGEgPSAyMDApCgoKIyBQcmludCBOZXR3b3JrcwpMX29iajQgPC0gTElNT05fRWRnZXNfTmV0d29ya3MoTF9vYmozLCB0aHJlc2hvbGQgPSAwLjAwMDIsIHZlcnRleC5zaXplID0gMywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcnRleC5sYWJlbC5jZXggPSA4LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVydGV4LmxhYmVsLmNvbG9yID0gImJsYWNrIikKYGBgCgoKIyMjIDIuNS4yIC0gVHJ1ZSBOZXR3b3JrcyAtIG5vIENvdmFyaWF0ZXMgIAoKU1BJRUMtRUFTSSBOZXR3b3JrcyBvZiB0aGUgUmF3IERhdGEgd2l0aG91dCBjb3ZhcmlhdGVzLiBUaGVzZSB3aWxsIGJlIGxhYmVsZWQgYXMgT19FZGdlcyAoZm9yIG9yaWdpbmFsIGVkZ2VzKS4gV2UgcmVhZCBpbiB0aGUgZGF0YSBhbmQgdGhlbiBhc3NpZ24gaXQgdG8gYSBkb3duc3RyZWFtIExJTU9uIG9iamVjdCB0byBydW4gU1BJRUMtRUFTSSBhY3Jvc3MgYWxsIHRoZSB0aW1lcG9pbnRzIGJ1dCBieXBhc3NpbmcgdGhlIG5ldHdvcmsgaW5mZXJlbmNlIHN0ZXAKYGBge3J9CiMgTWFrZSBmYWtlIExJTU9OIE9iamVjdCB0byBQYXNzIHRvIE5ldHdvcmsgSW5mZXJlbmNlIFN0ZXBzCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKb3JpZ2luYWxfZGF0YSA8LSByZWFkLmNzdihoZXJlKCJEYXRhIiwgIkdMVl9TaW1EYXRhIiwgIkRhdGFzZXRfNCIsIkdMVl8wLjkwLmNzdiIpKQpvcmlnaW5hbF9kYXRhIDwtIG9yaWdpbmFsX2RhdGEgJT4lIGNvbHVtbl90b19yb3duYW1lcygiWCIpCgojIExvb3AgdGhyb3VnaCB0aGUgdGltZSBwb2ludHMsIGZpbHRlciBkYXRhLCBhbmQgcmVtb3ZlIG1ldGFkYXRhCmNvdW50c19saXN0IDwtIGxpc3QoKQpmb3IgKGkgaW4gMToxMCkgewogIGZpbHRlcmVkX2NvdW50cyA8LSBvcmlnaW5hbF9kYXRhICU+JSBmaWx0ZXIoVGltZSA9PSBpKQogIGNvdW50c19saXN0W1tpXV0gPC0gcm91bmQoZmlsdGVyZWRfY291bnRzWywgNjo1NV0pCn0KCiMgSW5pdGlhbGl6ZSBMX29ial9zaW0yCkxfb2JqX3NpbTIgPC0gTF9vYmoyCgojIEFzc2lnbiB0aGUgcHJvY2Vzc2VkIGRhdGEgdG8gdGhlIGNvcnJlc3BvbmRpbmcgc2xvdHMgaW4gTF9vYmpfc2ltMgpmb3IgKGkgaW4gMToxMCkgewogIExfb2JqX3NpbTJbWyJDb3JyZWN0ZWRfQ291bnRzX1RpbWUiXV1bW3Bhc3RlMCgiQ29ycmVjdGVkX0NvdW50c18iLCBpKV1dIDwtIGNvdW50c19saXN0W1tpXV0KfQoKCiMgTmV0d29yayBJbmZlcmVuY2UKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNldCBzZWVkCnNldC5zZWVkKDEyMzQ1KQpwc2VlZCA8LSBsaXN0KHJlcC5udW09NTAsIHNlZWQ9MTAwMTApCiMgU1BJRUMtRUFTSSBwZXIgdGltZQpMX29ial9zaW0zIDwtIExJTU9OX05ldEluZl9UaW1lKE9iaiA9IExfb2JqX3NpbTIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJnbGFzc28iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWwuY3JpdGVyaW9uID0gImJzdGFycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFtYmRhLm1pbi5yYXRpbyA9IDAuMDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsc2FyLnNlbGVjdD1UUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxzYXIucGFyYW1zPXBzZWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5sYW1iZGEgPSAyMDApCgoKIyBQcmludCBOZXR3b3JrcwpMX29ial9zaW00IDwtIExJTU9OX0VkZ2VzX05ldHdvcmtzKExfb2JqX3NpbTMsIHRocmVzaG9sZCA9IDAuMDAwMiwgdmVydGV4LnNpemUgPSAzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVydGV4LmxhYmVsLmNleCA9IDgsIHZlcnRleC5sYWJlbC5jb2xvciA9ICJibGFjayIpCgoKCmBgYAoKCgojIyMgMi41LjMgLSBTUElFQy1FQVNJIG9uIENvdmFyaWF0ZSBEYXRhICAKU2ltaWxhciB0byBwcm9jZXNzIGZvciBvcmlnaW5hbCBuZXR3b3JrcywgZXhjZXB0IG5vdyB3ZSB3aWxsIHJ1biBpdCBvbiBjb3ZhcmlhdGUgY29uZmxhdGVkIGRhdGEuIFRoZXNlIHdpbGwgYmUgbGFiZWxlZCBhcyBDb3ZfRWRnZXMKYGBge3J9CiMgTWFrZSBmYWtlIExJTU9OIE9iamVjdCB0byBQYXNzIHRvIE5ldHdvcmsgSW5mZXJlbmNlIFN0ZXBzCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKb3JpZ2luYWxfZGF0YSA8LSByZWFkLmNzdihoZXJlKCJEYXRhIiwgIkdMVl9TaW1EYXRhIiwgIkRhdGFzZXRfNCIsIkdMVl9Db3ZfMC45MC5jc3YiKSkKb3JpZ2luYWxfZGF0YSA8LSBvcmlnaW5hbF9kYXRhICU+JSBjb2x1bW5fdG9fcm93bmFtZXMoIlgiKQoKIyBMb29wIHRocm91Z2ggdGhlIHRpbWUgcG9pbnRzLCBmaWx0ZXIgZGF0YSwgYW5kIHJlbW92ZSBtZXRhZGF0YQpjb3VudHNfbGlzdCA8LSBsaXN0KCkKZm9yIChpIGluIDE6MTApIHsKICBmaWx0ZXJlZF9jb3VudHMgPC0gb3JpZ2luYWxfZGF0YSAlPiUgZmlsdGVyKFRpbWUgPT0gaSkKICBjb3VudHNfbGlzdFtbaV1dIDwtIHJvdW5kKGZpbHRlcmVkX2NvdW50c1ssIDY6NTVdKQp9CgojIEluaXRpYWxpemUgTF9vYmpfc2ltMgpMX29ial9zaW0yIDwtIExfb2JqMgoKIyBBc3NpZ24gdGhlIHByb2Nlc3NlZCBkYXRhIHRvIHRoZSBjb3JyZXNwb25kaW5nIHNsb3RzIGluIExfb2JqX3NpbTIKZm9yIChpIGluIDE6MTApIHsKICBMX29ial9zaW0yW1siQ29ycmVjdGVkX0NvdW50c19UaW1lIl1dW1twYXN0ZTAoIkNvcnJlY3RlZF9Db3VudHNfIiwgaSldXSA8LSBjb3VudHNfbGlzdFtbaV1dCn0KCgojIE5ldHdvcmsgSW5mZXJlbmNlCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTZXQgc2VlZApzZXQuc2VlZCgxMjM0NSkKcHNlZWQgPC0gbGlzdChyZXAubnVtPTUwLCBzZWVkPTEwMDEwKQojIFNQSUVDLUVBU0kgcGVyIHRpbWUKTF9vYmpfc2ltMyA8LSBMSU1PTl9OZXRJbmZfVGltZShPYmogPSBMX29ial9zaW0yLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiZ2xhc3NvIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsLmNyaXRlcmlvbiA9ICJic3RhcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhbWJkYS5taW4ucmF0aW8gPSAwLjAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB1bHNhci5zZWxlY3Q9VFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsc2FyLnBhcmFtcz1wc2VlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBubGFtYmRhID0gMjAwKQoKCiMgUHJpbnQgTmV0d29ya3MKTF9vYmpfc2ltNSA8LSBMSU1PTl9FZGdlc19OZXR3b3JrcyhMX29ial9zaW0zLCB0aHJlc2hvbGQgPSAwLjAwMDIsIHZlcnRleC5zaXplID0gMywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcnRleC5sYWJlbC5jZXggPSA4LCB2ZXJ0ZXgubGFiZWwuY29sb3IgPSAiYmxhY2siKQoKCgpgYGAKCgojIyMgMi41LjQgLSBNZXJnZSBPdXRjb21lcwpDYWxjdWxhdGUgdGhlIHNpbWlsYXIgZWRnZXMgYWNyb3NzIHRoZSB0aHJlZSBkYXRhIHR5cGVzCmBgYHtyfQojIExJTU9OIERhdGEgLSBFeHRyYWN0IEVkZ2VzCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIEluaXRpYWxpemUgYW4gZW1wdHkgbGlzdCB0byBzdG9yZSB0aGUgZGF0YSBmcmFtZXMKTF9FZGdlc19MaXN0IDwtIGxpc3QoKQoKIyBMb29wIHRocm91Z2ggdGhlIGVkZ2UgdGFibGVzIGFuZCBwcm9jZXNzIGVhY2ggb25lCmZvciAoaSBpbiAxOjEwKSB7CiAgZWRnZV90YWJsZSA8LSBhcy5kYXRhLmZyYW1lKExfb2JqNFtbIkVkZ2VfVGFibGUiXV1bW3Bhc3RlMCgiRWRnZV9UYWJsZV8iLCBpKV1dKQogICMgQ2hlY2sgaWYgdGhlIGVkZ2UgdGFibGUgaXMgZW1wdHkKICBpZiAobnJvdyhlZGdlX3RhYmxlKSA9PSAwKSB7CiAgICAjIENyZWF0ZSBhIGRhdGEgZnJhbWUgd2l0aCBOQSB2YWx1ZXMKICAgIGVkZ2VfdGFibGUgPC0gZGF0YS5mcmFtZShFZGdlX3dlaWdodCA9IE5BLCBUaW1lID0gaSkKICB9IGVsc2UgewogICAgIyBSZW5hbWUgdGhlIGNvbHVtbiBhbmQgYWRkIHRoZSBUaW1lIGNvbHVtbgogICAgZWRnZV90YWJsZSA8LSBlZGdlX3RhYmxlICU+JSBkcGx5cjo6cmVuYW1lKCJMX0VkZ2Vfd2VpZ2h0Ij0gIkVkZ2Vfd2VpZ2h0IikKICAgIGVkZ2VfdGFibGUkVGltZSA8LSBpCiAgfQogICMgQXBwZW5kIHRoZSBwcm9jZXNzZWQgdGFibGUgdG8gdGhlIGxpc3QKICBMX0VkZ2VzX0xpc3RbW2ldXSA8LSBlZGdlX3RhYmxlCn0KIyBDb21iaW5lIGFsbCB0aGUgcHJvY2Vzc2VkIHRhYmxlcyBpbnRvIGEgc2luZ2xlIGRhdGEgZnJhbWUKTF9FZGdlcyA8LSBiaW5kX3Jvd3MoTF9FZGdlc19MaXN0KQoKIyBPcmlnaW5hbCBEYXRhICsgQ292IC0gRXh0cmFjdCBlZGdlcwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyBJbml0aWFsaXplIGFuIGVtcHR5IGxpc3QgdG8gc3RvcmUgdGhlIGRhdGEgZnJhbWVzCkNvdl9FZGdlc19MaXN0IDwtIGxpc3QoKQoKIyBMb29wIHRocm91Z2ggdGhlIGVkZ2UgdGFibGVzIGFuZCBwcm9jZXNzIGVhY2ggb25lCmZvciAoaSBpbiAxOjEwKSB7CiAgZWRnZV90YWJsZSA8LSBhcy5kYXRhLmZyYW1lKExfb2JqX3NpbTVbWyJFZGdlX1RhYmxlIl1dW1twYXN0ZTAoIkVkZ2VfVGFibGVfIiwgaSldXSkKICAjIENoZWNrIGlmIHRoZSBlZGdlIHRhYmxlIGlzIGVtcHR5CiAgaWYgKG5yb3coZWRnZV90YWJsZSkgPT0gMCkgewogICAgIyBDcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggTkEgdmFsdWVzCiAgICBlZGdlX3RhYmxlIDwtIGRhdGEuZnJhbWUoRWRnZV93ZWlnaHQgPSBOQSwgVGltZSA9IGkpCiAgfSBlbHNlIHsKICAgICMgUmVuYW1lIHRoZSBjb2x1bW4gYW5kIGFkZCB0aGUgVGltZSBjb2x1bW4KICAgIGVkZ2VfdGFibGUgPC0gZWRnZV90YWJsZSAlPiUgZHBseXI6OnJlbmFtZSgiQ292X0VkZ2Vfd2VpZ2h0Ij0gIkVkZ2Vfd2VpZ2h0IikKICAgIGVkZ2VfdGFibGUkVGltZSA8LSBpCiAgfQogICMgQXBwZW5kIHRoZSBwcm9jZXNzZWQgdGFibGUgdG8gdGhlIGxpc3QKICBDb3ZfRWRnZXNfTGlzdFtbaV1dIDwtIGVkZ2VfdGFibGUKfQojIENvbWJpbmUgYWxsIHRoZSBwcm9jZXNzZWQgdGFibGVzIGludG8gYSBzaW5nbGUgZGF0YSBmcmFtZQpDb3ZfRWRnZXMgPC0gYmluZF9yb3dzKENvdl9FZGdlc19MaXN0KQoKIyBPcmlnaW5hbCBEYXRhIC0gRXh0cmFjdCBFZGdlcwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIEluaXRpYWxpemUgYW4gZW1wdHkgbGlzdCB0byBzdG9yZSB0aGUgZGF0YSBmcmFtZXMKT19FZGdlc19MaXN0IDwtIGxpc3QoKQoKIyBMb29wIHRocm91Z2ggdGhlIGVkZ2UgdGFibGVzIGFuZCBwcm9jZXNzIGVhY2ggb25lCmZvciAoaSBpbiAxOjEwKSB7CiAgZWRnZV90YWJsZSA8LSBhcy5kYXRhLmZyYW1lKExfb2JqX3NpbTRbWyJFZGdlX1RhYmxlIl1dW1twYXN0ZTAoIkVkZ2VfVGFibGVfIiwgaSldXSkKICAjIENoZWNrIGlmIHRoZSBlZGdlIHRhYmxlIGlzIGVtcHR5CiAgaWYgKG5yb3coZWRnZV90YWJsZSkgPT0gMCkgewogICAgIyBDcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggTkEgdmFsdWVzCiAgICBlZGdlX3RhYmxlIDwtIGRhdGEuZnJhbWUoRWRnZV93ZWlnaHQgPSBOQSwgVGltZSA9IGkpCiAgfSBlbHNlIHsKICAgICMgUmVuYW1lIHRoZSBjb2x1bW4gYW5kIGFkZCB0aGUgVGltZSBjb2x1bW4KICAgIGVkZ2VfdGFibGUgPC0gZWRnZV90YWJsZSAlPiUgZHBseXI6OnJlbmFtZSgiT19FZGdlX3dlaWdodCI9ICJFZGdlX3dlaWdodCIpCiAgICBlZGdlX3RhYmxlJFRpbWUgPC0gaQogIH0KICAjIEFwcGVuZCB0aGUgcHJvY2Vzc2VkIHRhYmxlIHRvIHRoZSBsaXN0CiAgT19FZGdlc19MaXN0W1tpXV0gPC0gZWRnZV90YWJsZQp9CiMgQ29tYmluZSBhbGwgdGhlIHByb2Nlc3NlZCB0YWJsZXMgaW50byBhIHNpbmdsZSBkYXRhIGZyYW1lCk9fRWRnZXMgPC0gYmluZF9yb3dzKE9fRWRnZXNfTGlzdCkKCiMgQmluZCBkYXRhIHRvZ2V0aGVyCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCk1lcmdlZF9UMSA8LSBtZXJnZShMX0VkZ2VzLCBPX0VkZ2VzLCBieS55ID0gYygiU291cmNlIiwgIlNpbmsiLCAiVGltZSIpLCBhbGw9VFJVRSkKTWVyZ2VkX1QxIDwtIG1lcmdlKE1lcmdlZF9UMSwgQ292X0VkZ2VzLCBieS55ID0gYygiU291cmNlIiwgIlNpbmsiLCAiVGltZSIpLCBhbGw9VFJVRSkKCgojIENvbXBhcmlzb24gR3JhcGggRGF0YQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyBJZGVudGlmeSB0cnVlIGVkZ2VzCk1lcmdlZF9UOTAgPC0gTWVyZ2VkX1QxICU+JQogIGVkZ2VfcmVjb3ZlcnkoKSAKCiMgQWRkIENvbm5lY3RhbmNlIApNZXJnZWRfVDkwJENvbm5lY3RhbmNlIDwtIDAuOTAKYGBgCgoKIyAzIC0gU3VtbWFyeSBvZiBTZW5zaXRpdnR5IEFuYWx5c2lzCioqKgpNZXJnZSBkYXRhIGZvciB0aGUgZm9sbG93aW5nIGdyYXBoaWNhbCBhbmFseXNpcwpgYGB7cn0KIyBEcm9wIFNvdXJjZSBhbmQgU2luayBjb2x1bW4gYW5kIGNvbWJpbmUgdG9nZXRoZXIKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKTWVyZ2VkX1QxYiA8LSBNZXJnZWRfVDEwICU+JSBkcGx5cjo6c2VsZWN0KC1Tb3VyY2UsIC1TaW5rKQpNZXJnZWRfVDIwYiA8LSBNZXJnZWRfVDIwICU+JSBkcGx5cjo6c2VsZWN0KC1Tb3VyY2UsIC1TaW5rKQpNZXJnZWRfVDUwYiA8LSBNZXJnZWRfVDUwICU+JSBkcGx5cjo6c2VsZWN0KC1Tb3VyY2UsIC1TaW5rKQpNZXJnZWRfVDc1YiA8LSBNZXJnZWRfVDc1ICU+JSBkcGx5cjo6c2VsZWN0KC1Tb3VyY2UsIC1TaW5rKQpNZXJnZWRfVDkwYiA8LSBNZXJnZWRfVDkwICU+JSBkcGx5cjo6c2VsZWN0KC1Tb3VyY2UsIC1TaW5rKQoKIyBNZXJnZSB0b2dldGhlcgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIERhdGEgZm9yIEdyYXBocyAxIGFuZCAyClN1YmplY3Rfc2Vuc19kYXRhX2Z1bGwgPC0gcmJpbmQoTWVyZ2VkX1QxYiwgTWVyZ2VkX1QyMGIsIE1lcmdlZF9UNTBiLCBNZXJnZWRfVDc1YiwgTWVyZ2VkX1Q5MGIpCgojIERhdGEgZm9yIEdyYXBoIDMKI01lcmdlZF9UOTBjIDwtIE1lcmdlZF9UOTAgJT4lIGRwbHlyOjpzZWxlY3QoLUVkZ2Vfd2VpZ2h0KQpNZXJnZWRfVDEwYyA8LSBNZXJnZWRfVDEwWyxjKCJTb3VyY2UiLCJTaW5rIiwiVGltZSIsIkxfRWRnZV93ZWlnaHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIk9fRWRnZV93ZWlnaHQiLCJDb3ZfRWRnZV93ZWlnaHQiLCJSZWNvdmVyeSIsIkNvbm5lY3RhbmNlIildClN1YmplY3Rfc2Vuc19kYXRhX3RheGEgPC0gcmJpbmQoTWVyZ2VkX1QxMGMsIE1lcmdlZF9UMjAsIE1lcmdlZF9UNTAsIE1lcmdlZF9UNzUsIE1lcmdlZF9UOTApCmBgYAoKCk9wdGlvbiB0byB3cml0ZSBhbmQgcmVhZCBpbiB0aGUgYWJvdmUgZmlsZXMKYGBge3J9CiMjd3JpdGUuY3N2KFN1YmplY3Rfc2Vuc19kYXRhX2Z1bGwsIGhlcmUoIk91dHB1dCIsICJEYXRhc2V0XzQiLCAiU3ViamVjdF9zZW5zX2RhdGFfZnVsbC5jc3YiKSkKI3dyaXRlLmNzdihTdWJqZWN0X3NlbnNfZGF0YV90YXhhLCBoZXJlKCJPdXRwdXQiLCAiRGF0YXNldF80IiwiU3ViamVjdF9zZW5zX2RhdGFfdGF4YS5jc3YiKSkKClN1YmplY3Rfc2Vuc19kYXRhX2Z1bGwgPC0gcmVhZC5jc3YoaGVyZSgiT3V0cHV0IiwgIkRhdGFzZXRfNCIsICJTdWJqZWN0X3NlbnNfZGF0YV9mdWxsLmNzdiIpKQpTdWJqZWN0X3NlbnNfZGF0YV90YXhhIDwtIHJlYWQuY3N2KGhlcmUoIk91dHB1dCIsIkRhdGFzZXRfNCIsICJTdWJqZWN0X3NlbnNfZGF0YV90YXhhLmNzdiIpKQpgYGAKCgoKCiMjIDMuMSBHcmFwaCBQZXJjZW50IFRydWUgRWRnZXMKCkZpbmQgdGhlIFBlcmNlbnQgVHJ1ZSBFZGdlcyBSZWNvdmVyZWQgcGVyIHNhbXBsZSBzaXplIHBlciB0aW1lIHBvaW50CgoKIyMjIDMuMS4xIC0gRGF0YSBGb3JtYXR0aW5nCgpgYGB7cn0KIyBMb29wIHRocm91Z2ggYWxsIHRpbWVwb2ludHMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwphbGxfdGltZXBvaW50cyA8LSBsaXN0KCkKCmZvciAodGltZSBpbiAxOjEwKSB7CiAgIyBGaWx0ZXIgZGF0YSBmb3IgdGhlIGN1cnJlbnQgU2FtcGxlIFNpemUKICBTdWJqZWN0X3NlbnNfZGF0YSA8LSBTdWJqZWN0X3NlbnNfZGF0YV9mdWxsICU+JSBmaWx0ZXIoVGltZSA9PSB0aW1lKQogIAogICMgQ291bnR0aGUgdG90YWwgVHJ1ZV9FZGdlcyBmb3IgdGhhdCBTYW1wbGUgU2l6ZQogICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKICAKICBTdWJqZWN0X3NlbnNfZGF0YTIgPC0gU3ViamVjdF9zZW5zX2RhdGEgJT4lCiAgICBncm91cF9ieShDb25uZWN0YW5jZSkgJT4lCiAgICBzdW1tYXJpc2UoVHJ1ZV9FZGdlcyA9IHN1bShPX0VkZ2Vfd2VpZ2h0ICE9IDAsIG5hLnJtID0gVFJVRSkpICU+JQogICAgdW5ncm91cCgpCiAgCiAgIyBNZXJnZSB0aGUgc3VtbWFyaXplZCBkYXRhIGJhY2sgd2l0aCB0aGUgb3JpZ2luYWwgZGF0YQogIFN1YmplY3Rfc2Vuc19kYXRhIDwtIG1lcmdlKFN1YmplY3Rfc2Vuc19kYXRhLCBTdWJqZWN0X3NlbnNfZGF0YTIsIGJ5PSJDb25uZWN0YW5jZSIsIGFsbD1UUlVFKQogIAogICMgRmluZCB0aGUgUGVyY2VudCBUcnVlIEVkZ2VzIERhdGEKICAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiAgCiAgIyBjb3VudCB0aGUgbnVtYmVyIG9mIGVkZ2VzCiAgUGVyY2VudF90cnVlIDwtIFN1YmplY3Rfc2Vuc19kYXRhICAlPiUKICAgIGdyb3VwX2J5KENvbm5lY3RhbmNlLCBSZWNvdmVyeSwgVHJ1ZV9FZGdlcykgJT4lCiAgICBzdW1tYXJpc2UoQ291bnQgPSBuKCkpICU+JQogICAgdW5ncm91cCgpICU+JQogICAgZmlsdGVyKFJlY292ZXJ5ICE9IDApCiAgCiAgIyBBZGQgdGhlIG51bWJlciBvZiBlZGdlcyBpbiBhbGwgdGhyZWUgZGF0YSB0eXBlcyB0byB0aGUgTElNT04gdnMgUmF3IFNwZWljLUVhc2kgY291bnRzCiAgUGVyY2VudF90cnVlIDwtIFBlcmNlbnRfdHJ1ZSAlPiUKICAgIGdyb3VwX2J5KENvbm5lY3RhbmNlLCBUcnVlX0VkZ2VzKSAlPiUKICAgIG11dGF0ZSgKICAgICAgQ291bnRfMyA9IGlmZWxzZShSZWNvdmVyeSA9PSAzLCBDb3VudCwgMCksCiAgICAgIENvdW50ID0gaWZlbHNlKFJlY292ZXJ5ID09IDEgfCBSZWNvdmVyeSA9PSAyLCBDb3VudCArIHN1bShDb3VudF8zKSwgQ291bnQpCiAgICApICU+JQogICAgZHBseXI6OnNlbGVjdCgtQ291bnRfMykKICAKICAjIENyZWF0ZSBhIGRhdGFmcmFtZSB3aXRoIGFsbCBjb21iaW5hdGlvbnMgb2YgQ29ubmVjdGFuY2UgYW5kIFJlY292ZXJ5CiAgY29tYmluYXRpb25zIDwtIGV4cGFuZC5ncmlkKENvbm5lY3RhbmNlID0gdW5pcXVlKFBlcmNlbnRfdHJ1ZSRDb25uZWN0YW5jZSksIFJlY292ZXJ5ID0gYygxLCAyLCAzKSkKICBQZXJjZW50X3RydWUgPC0gbWVyZ2UoUGVyY2VudF90cnVlLCBjb21iaW5hdGlvbnMsIGJ5PWMoIkNvbm5lY3RhbmNlIiwgIlJlY292ZXJ5IiksIGFsbD1UUlVFKQogIAogICMgRmlsbCBhbmQgcmVwbGFjZSBtaXNzaW5nIHZhbHVlcwogIFBlcmNlbnRfdHJ1ZSA8LSBQZXJjZW50X3RydWUgJT4lCiAgICBncm91cF9ieShDb25uZWN0YW5jZSkgJT4lCiAgICBmaWxsKFRydWVfRWRnZXMsIC5kaXJlY3Rpb24gPSAiZG93bnVwIikgJT4lCiAgICBtdXRhdGUoCiAgICAgIENvdW50ID0gaWZlbHNlKGlzLm5hKENvdW50KSwgQ291bnRbUmVjb3ZlcnkgPT0gM11bMV0sIGlmZWxzZShpcy5uYShDb3VudCksIDAsIENvdW50KSkKICAgICkgJT4lCiAgICB1bmdyb3VwKCkgJT4lCiAgICByZXBsYWNlX25hKGxpc3QoQ291bnQgPSAwKSkgJT4lIAogICAgbXV0YXRlKERhdGFUeXBlID0gY2FzZV93aGVuKAogICAgICBSZWNvdmVyeSA9PSAxIH4gIkxJTU9OIiwKICAgICAgUmVjb3ZlcnkgPT0gMiB+ICJTUElFQy1FQVNJIiwKICAgICAgUmVjb3ZlcnkgPT0gMyB+ICJCb3RoIgogICAgKSkKICAKICAjIENhbGN1bGF0ZSB0aGUgUGVyY2VudGFnZSBvZiBlZGdlcwogIFBlcmNlbnRfdHJ1ZSRQZXJjZW50X3RydWUgPC0gUGVyY2VudF90cnVlJENvdW50IC8gUGVyY2VudF90cnVlJFRydWVfRWRnZXMKICBQZXJjZW50X3RydWUkVGltZSA8LSB0aW1lCiAgCiAgIyBTdG9yZSB0aGUgcmVzdWx0IGluIHRoZSBsaXN0CiAgYWxsX3RpbWVwb2ludHNbW3RpbWVdXSA8LSBQZXJjZW50X3RydWUKfQoKIyBDb21iaW5lIGFsbCB0aW1lcG9pbnRzIHRvZ2V0aGVyClBlcmNlbnRfdHJ1ZSA8LSBkby5jYWxsKHJiaW5kLCBhbGxfdGltZXBvaW50cykKCiMgRmlsdGVyIHRoZSBjb21iaW5lZCBkYXRhClBlcmNlbnRfdHJ1ZSA8LSBQZXJjZW50X3RydWUgJT4lIGZpbHRlcihEYXRhVHlwZSAhPSAiQm90aCIpCmBgYAoKCgoKIyMjIDMuMS4yIC0gU3RhdGlzdGljcwoKQ2hlY2sgbm9ybWFsY3kgdG8gZGV0ZXJtaW5lIGlmIHQtdGVzdCBvciBpZiB3aWxjb3ggcmFuayBzdW0KYGBge3J9CiMgU3RlcCAxIC0gUnVuIFNoYXBpcm8gV2lsa3MgdG8gdGVzdCBmb3Igbm9ybWFsYWN5IG9mIHRoZSBkYXRhCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnNoYXBpcm9fcmVzdWx0IDwtIHNoYXBpcm8udGVzdChQZXJjZW50X3RydWUkUGVyY2VudF90cnVlKQpwX3ZhbHVlIDwtIHNoYXBpcm9fcmVzdWx0JHAudmFsdWUKCiMgU3RlcCAyIC0gUHJpbnQgcC12YWx1ZSBvbiB0aGUgaGlzdG9ncmFtCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgQ3JlYXRlIHRoZSBoaXN0b2dyYW0gcGxvdApoaXN0KFBlcmNlbnRfdHJ1ZSRQZXJjZW50X3RydWUsIGJyZWFrcz0xMDAsIAogICAgIG1haW4gPSAiRGlzdHJpYnV0aW9uIG9mIFBlcmNlbnQgVHJ1ZSIsIAogICAgIHhsYWIgPSAiUGVyY2VudCBUcnVlIiwgeWxhYiA9ICJGcmVxdWVuY3kiKQojIEFkZCB0aGUgcC12YWx1ZSBhbm5vdGF0aW9uCnRleHQoeCA9IDAuNCwgeSA9IDEwLCAKICAgICBsYWJlbHMgPSBwYXN0ZSgiU2hhcGlyby1XaWxrIHAtdmFsdWU6ICIsIHBfdmFsdWUpKQoKYGBgCgpEYXRhIGlzIG5vdCBub3JtYWxseSBkaXN0cmlidXRlZCBzbyB1c2Ugd2lsY294IHJhbmsgc3VtIHRlc3QgaW5zdGVhZCBvZiB0LXRlc3QKCgpwLXZhbHVlIGxhYmVsIGZ1bmN0aW9uCmBgYHtyfQojIFAtdmFsdWUgbGFiZWxzCnBfdmFsdWVfc2lnIDwtIGZ1bmN0aW9uKHApIHsKICBpZiAoaXMubmEocCkpIHsKICAgIHJldHVybigiICIpCiAgfSBlbHNlIGlmIChwIDwgMC4wMDEpIHsKICAgIHJldHVybigiKioqIikKICB9IGVsc2UgaWYgKHAgPCAwLjAxKSB7CiAgICByZXR1cm4oIioqIikKICB9IGVsc2UgaWYgKHAgPCAwLjA1KSB7CiAgICByZXR1cm4oIioiKQogIH0gZWxzZSB7CiAgICByZXR1cm4oIiAiKQogIH0KfQpgYGAKCgpQbG90IHRoZSBmaW5kaW5ncwpgYGB7cn0KCiMgU3RlcCAxOiBTdW1tYXJ5IFN0YXRpc3RpY3MKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCiMgUGVyZm9ybSBhIFQtdGVzdHMgYW5kIG1ha2Ugc3VtbWFyeSBzdGF0aXN0aWNzIGZvciBwbG90dGluZwpTdW1tYXJ5X2RhdGEgPC0gUGVyY2VudF90cnVlICU+JQogIGdyb3VwX2J5KENvbm5lY3RhbmNlKSAlPiUKICBkbyh7CiAgICBkYXRhID0gLgogICAgCiAgICAjIENoZWNrIGlmIHRoZSBkYXRhIGZvciBhbnkgZ3JvdXAgaXMgY29uc3RhbnQKICAgIGdyb3VwX3ZhcmlhbmNlcyA8LSBkYXRhICU+JQogICAgICBncm91cF9ieShEYXRhVHlwZSkgJT4lCiAgICAgIHN1bW1hcmlzZSh2YXIgPSB2YXIoUGVyY2VudF90cnVlLCBuYS5ybSA9IFRSVUUpKSAgIyBIYW5kbGUgTkFzIGluIHZhcmlhbmNlIGNhbGN1bGF0aW9uCiAgICAKICAgIGlmIChhbnkoaXMubmEoZ3JvdXBfdmFyaWFuY2VzJHZhcikpIHx8IGFueShncm91cF92YXJpYW5jZXMkdmFyID09IDApKSB7CiAgICAgICMgSWYgZGF0YSBpcyBjb25zdGFudCBvciB2YXJpYW5jZSBjYWxjdWxhdGlvbiByZXN1bHRlZCBpbiBOQSwgYXNzaWduIHAtdmFsdWUgYXMgTkEKICAgICAgcF92YWx1ZSA8LSBOQQogICAgfSBlbHNlIHsKICAgICAgIyBQZXJmb3JtIHQtdGVzdAogICAgICB3aWxjb3ggPC0gZXhhY3RSYW5rVGVzdHM6OndpbGNveC5leGFjdChQZXJjZW50X3RydWUgfiBEYXRhVHlwZSwgZGF0YSA9IGRhdGEpCiAgICAgIHBfdmFsdWUgPC0gdGlkeSh3aWxjb3gpJHAudmFsdWUKICAgIH0KICAgIAogICAgc3VtbWFyaXNlKGRhdGEsCiAgICAgIG1lYW5fcGVyY2VudF90cnVlX0xJTU9OID0gbWVhbihQZXJjZW50X3RydWVbRGF0YVR5cGUgPT0gIkxJTU9OIl0sIG5hLnJtID0gVFJVRSksCiAgICAgIHNkX3BlcmNlbnRfdHJ1ZV9MSU1PTiA9IHNkKFBlcmNlbnRfdHJ1ZVtEYXRhVHlwZSA9PSAiTElNT04iXSwgbmEucm0gPSBUUlVFKSwKICAgICAgbl9MSU1PTiA9IHN1bShEYXRhVHlwZSA9PSAiTElNT04iLCBuYS5ybSA9IFRSVUUpLAogICAgICBtZWFuX3BlcmNlbnRfdHJ1ZV9TUElFQ0VBU0kgPSBtZWFuKFBlcmNlbnRfdHJ1ZVtEYXRhVHlwZSA9PSAiU1BJRUMtRUFTSSJdLCBuYS5ybSA9IFRSVUUpLAogICAgICBzZF9wZXJjZW50X3RydWVfU1BJRUNFQVNJID0gc2QoUGVyY2VudF90cnVlW0RhdGFUeXBlID09ICJTUElFQy1FQVNJIl0sIG5hLnJtID0gVFJVRSksCiAgICAgIG5fU1BJRUNFQVNJID0gc3VtKERhdGFUeXBlID09ICJTUElFQy1FQVNJIiwgbmEucm0gPSBUUlVFKSwKICAgICAgcC52YWx1ZSA9IHBfdmFsdWUKICAgICkKICB9KSAlPiUKICBtdXRhdGUoCiAgICBzZV9wZXJjZW50X3RydWVfTElNT04gPSBzZF9wZXJjZW50X3RydWVfTElNT04gLyBzcXJ0KG5fTElNT04pLAogICAgc2VfcGVyY2VudF90cnVlX1NQSUVDRUFTSSA9IHNkX3BlcmNlbnRfdHJ1ZV9TUElFQ0VBU0kgLyBzcXJ0KG5fU1BJRUNFQVNJKSwKICAgIHNpZ25pZmljYW5jZSA9IHNhcHBseShwLnZhbHVlLCBwX3ZhbHVlX3NpZykKICApCgojIFN0ZXAgMjogTWFrZSB0aGUgcGxvdHRpbmcgZGF0YQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpTdW1tYXJ5X2RhdGFfbG9uZyA8LSBTdW1tYXJ5X2RhdGEgJT4lCiAgZHBseXI6OnNlbGVjdChDb25uZWN0YW5jZSwgc3RhcnRzX3dpdGgoIm1lYW5fcGVyY2VudF90cnVlIiksIHN0YXJ0c193aXRoKCJzZV9wZXJjZW50X3RydWUiKSwgc2lnbmlmaWNhbmNlKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IHN0YXJ0c193aXRoKCJtZWFuX3BlcmNlbnRfdHJ1ZSIpLCBuYW1lc190byA9ICJEYXRhVHlwZSIsIHZhbHVlc190byA9ICJtZWFuX3BlcmNlbnRfdHJ1ZSIpICU+JQogIG11dGF0ZShEYXRhVHlwZSA9IGlmZWxzZShzdHJfZGV0ZWN0KERhdGFUeXBlLCAiTElNT04iKSwgIkxJTU9OIiwgIlNQSUVDLUVBU0kiKSkgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgic2VfcGVyY2VudF90cnVlIiksIG5hbWVzX3RvID0gIkRhdGFUeXBlX3NlIiwgdmFsdWVzX3RvID0gInNlX3BlcmNlbnRfdHJ1ZSIpICU+JQogIG11dGF0ZShEYXRhVHlwZV9zZSA9IGlmZWxzZShzdHJfZGV0ZWN0KERhdGFUeXBlX3NlLCAiTElNT04iKSwgIkxJTU9OIiwgIlNQSUVDLUVBU0kiKSkgJT4lCiAgZmlsdGVyKERhdGFUeXBlID09IERhdGFUeXBlX3NlKSAlPiUKICBkcGx5cjo6c2VsZWN0KC1EYXRhVHlwZV9zZSkKCiMgU3RlcCAzOiBGaW5hbCBmaWd1cmUKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKZ2dwbG90KFN1bW1hcnlfZGF0YV9sb25nLCBhZXMoeCA9IENvbm5lY3RhbmNlLCB5ID0gbWVhbl9wZXJjZW50X3RydWUsIGNvbG9yID0gRGF0YVR5cGUpKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbiA9IG1lYW5fcGVyY2VudF90cnVlIC0gc2VfcGVyY2VudF90cnVlLCB5bWF4ID0gbWVhbl9wZXJjZW50X3RydWUgKyBzZV9wZXJjZW50X3RydWUpLCB3aWR0aCA9IDAuMDEpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gc2lnbmlmaWNhbmNlLCB5ID0gMC45KSwgc2l6ZSA9IDUsIHZqdXN0ID0gLTAuNSwgY2hlY2tfb3ZlcmxhcCA9IFRSVUUsIGNvbG9yID0gImJsYWNrIikgKyAjIHAtdmFsdWUgYW5ub3RhdGlvbnMKICBsYWJzKHggPSAiQ29ubmVjdGFuY2UiLCB5ID0gIlBlcmNlbnRfdHJ1ZSIsIGNvbG9yID0gIkRhdGFUeXBlIikgKwogIHlsaW0oMCwgMSkgKyAKICB4bGFiKCJDb25uZWN0YW5jZSIpICsKICB5bGFiKCJQZXJjZW50IFJlY292ZXJlZCBFZGdlcyIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZSA9ICJOZXR3b3JrIiwgdmFsdWVzID0gYygiTElNT04iID0gIm9yYW5nZSIsICJTUElFQy1FQVNJIiA9ICJibHVlIikpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLjEwLCAxLjAwKSwgYnJlYWtzID0gYygwLjEwLDAuMjAsMC41MCwwLjc1LDAuOTApKSAgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gImFyaWFsIixjb2xvciA9ICJibGFjayIsIHNpemUgPSAxOCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gImFyaWFsIixjb2xvciA9ICJibGFjayIsIHNpemUgPSAxOCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJhcmlhbCIsY29sb3IgPSAiYmxhY2siLCBzaXplID0gMTQpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiYXJpYWwiLGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDE0KSkKYGBgCgoKCgoKIyMgMy4yICBHcmFwaCBUb3RhbCBFZGdlcwoKIyMjIDMuMi4xIC0gRGF0YSBGb3JtYXR0aW5nCk1ha2UgRGF0YSBmcmFtZSBmb3IgVG90YWwgRWRnZXMgcGVyIGRhdGEgVHlwZSBieSBUaW1lIHBvaW50CmBgYHtyfQojIEluaXRpYWxpemUgYW4gZW1wdHkgbGlzdCB0byBzdG9yZSByZXN1bHRzCnRvdGFsX2VkZ2VzX2xpc3QgPC0gbGlzdCgpCgojIExvb3AgdGhyb3VnaCBlYWNoIHRpbWVwb2ludCBmcm9tIDEgdG8gMTAKZm9yICh0aW1lIGluIDE6MTApIHsKICAjIENhbGN1bGF0ZSBUb3RhbCBFZGdlcyBmb3IgdGhlIGN1cnJlbnQgdGltZXBvaW50CiAgdG90YWxfZWRnZXMgPC0gU3ViamVjdF9zZW5zX2RhdGFfZnVsbCAlPiUKICAgIGZpbHRlcihUaW1lID09IHRpbWUpICU+JQogICAgZ3JvdXBfYnkoQ29ubmVjdGFuY2UpICU+JQogICAgc3VtbWFyaXplKAogICAgICBDb3VudF9MX0VkZ2Vfd2VpZ2h0ID0gc3VtKCFpcy5uYShMX0VkZ2Vfd2VpZ2h0KSksCiAgICAgIENvdW50X09fRWRnZV93ZWlnaHQgPSBzdW0oIWlzLm5hKE9fRWRnZV93ZWlnaHQpKSwKICAgICAgQ291bnRfQ292X0VkZ2Vfd2VpZ2h0ID0gc3VtKCFpcy5uYShDb3ZfRWRnZV93ZWlnaHQpKQogICAgKSAlPiUKICAgIHBpdm90X2xvbmdlcihjb2xzID0gc3RhcnRzX3dpdGgoIkNvdW50IiksIG5hbWVzX3RvID0gIkNvdW50c19Db2x1bW5fTmFtZSIsIHZhbHVlc190byA9ICJDb3VudCIpICU+JQogICAgbXV0YXRlKERhdGFUeXBlID0gY2FzZV93aGVuKAogICAgICBDb3VudHNfQ29sdW1uX05hbWUgPT0gIkNvdW50X0xfRWRnZV93ZWlnaHQiIH4gIkxJTU9OIiwKICAgICAgQ291bnRzX0NvbHVtbl9OYW1lID09ICJDb3VudF9PX0VkZ2Vfd2VpZ2h0IiB+ICJUcnVlIiwKICAgICAgQ291bnRzX0NvbHVtbl9OYW1lID09ICJDb3VudF9Db3ZfRWRnZV93ZWlnaHQiIH4gIlNQSUVDLUVBU0kiCiAgICApKQogIHRvdGFsX2VkZ2VzJFRpbWUgPC0gdGltZQogIAogICMgU3RvcmUgdGhlIHJlc3VsdCBpbiB0aGUgbGlzdAogIHRvdGFsX2VkZ2VzX2xpc3RbW3RpbWVdXSA8LSB0b3RhbF9lZGdlcwp9CgojIENvbWJpbmUgYWxsIHRpbWVwb2ludHMgdG9nZXRoZXIKVG90YWxfRWRnZXMgPC0gZG8uY2FsbChyYmluZCwgdG90YWxfZWRnZXNfbGlzdCkKCmBgYAoKCiMjIyAzLjIuMiAtIFN0YXRpc3RpY3MKCgpDaGVjayBub3JtYWxjeSB0byBkZXRlcm1pbmUgaWYgdC10ZXN0IG9yIGlmIHdpbGNveCByYW5rIHN1bQpgYGB7cn0KIyBTdGVwIDEgLSBSdW4gU2hhcGlybyBXaWxrcyB0byB0ZXN0IGZvciBub3JtYWxhY3kgb2YgdGhlIGRhdGEKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKc2hhcGlyb19yZXN1bHQgPC0gc2hhcGlyby50ZXN0KFRvdGFsX0VkZ2VzJENvdW50KQpwX3ZhbHVlIDwtIHNoYXBpcm9fcmVzdWx0JHAudmFsdWUKCiMgU3RlcCAyIC0gUHJpbnQgcC12YWx1ZSBvbiB0aGUgaGlzdG9ncmFtCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgQ3JlYXRlIHRoZSBoaXN0b2dyYW0gcGxvdApoaXN0KFRvdGFsX0VkZ2VzJENvdW50LCBicmVha3M9MTAwLCAKICAgICBtYWluID0gIlRvdGFsIEVkZ2VzIiwgCiAgICAgeGxhYiA9ICJDb3VudCBvZiBlZGdlcyIsIHlsYWIgPSAiRnJlcXVlbmN5IikKIyBBZGQgdGhlIHAtdmFsdWUgYW5ub3RhdGlvbgp0ZXh0KHggPSAxMDAsIHkgPSAxMCwgCiAgICAgbGFiZWxzID0gcGFzdGUoIlNoYXBpcm8tV2lsayBwLXZhbHVlOiAiLCBwX3ZhbHVlKSkKCmBgYAoKRGF0YSBpcyBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQsIHVzZSBXaWxjb3ggUmFuayBzdW0gaXNudGVhZCBvZiBULXRlc3QKCgoKYGBge3J9CiMgU3RlcCAxOiBTdW1tYXJ5IFN0YXRpc3RpY3MKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKU3VtbWFyeV9kYXRhIDwtIFRvdGFsX0VkZ2VzICU+JQogIGdyb3VwX2J5KENvbm5lY3RhbmNlKSAlPiUKICBzdW1tYXJpc2UoCiAgICAjIExJTU9OCiAgICBtZWFuX2VkZ2VzX0xJTU9OID0gbWVhbihDb3VudFtEYXRhVHlwZSA9PSAiTElNT04iXSwgbmEucm0gPSBUUlVFKSwKICAgIHNkX2VkZ2VzX0xJTU9OID0gc2QoQ291bnRbRGF0YVR5cGUgPT0gIkxJTU9OIl0sIG5hLnJtID0gVFJVRSksCiAgICBuX0xJTU9OID0gc3VtKERhdGFUeXBlID09ICJMSU1PTiIsIG5hLnJtID0gVFJVRSksCiAgICAKICAgICMgU1BJRUMtRUFTSSArIENPVgogICAgbWVhbl9lZGdlc19TUElFQ0VBU0kgPSBtZWFuKENvdW50W0RhdGFUeXBlID09ICJTUElFQy1FQVNJIl0sIG5hLnJtID0gVFJVRSksCiAgICBzZF9lZGdlc19TUElFQ0VBU0kgPSBzZChDb3VudFtEYXRhVHlwZSA9PSAiU1BJRUMtRUFTSSJdLCBuYS5ybSA9IFRSVUUpLAogICAgbl9TUElFQ0VBU0kgPSBzdW0oRGF0YVR5cGUgPT0gIlNQSUVDLUVBU0kiLCBuYS5ybSA9IFRSVUUpLAogICAgCiAgICAjIFNQSUVDLUVBU0kgbm8gQ09WCiAgICBtZWFuX2VkZ2VzX1RSVUUgPSBtZWFuKENvdW50W0RhdGFUeXBlID09ICJUcnVlIl0sIG5hLnJtID0gVFJVRSksCiAgICBzZF9lZGdlc19UUlVFID0gc2QoQ291bnRbRGF0YVR5cGUgPT0gIlRydWUiXSwgbmEucm0gPSBUUlVFKSwKICAgIG5fVFJVRSA9IHN1bShEYXRhVHlwZSA9PSAiVHJ1ZSIsIG5hLnJtID0gVFJVRSksCiAgICAKICAgICMgcC12YWx1ZXMKICAgIHBfdmFsdWVfTElNT05fVFJVRSA9IGlmIChuX0xJTU9OID4gMCAmIG5fVFJVRSA+IDApIAogICAgICB0aWR5KGV4YWN0UmFua1Rlc3RzOjp3aWxjb3guZXhhY3QoQ291bnRbRGF0YVR5cGUgJWluJSBjKCJMSU1PTiIsICJUcnVlIildIH4gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERhdGFUeXBlW0RhdGFUeXBlICVpbiUgYygiTElNT04iLCAiVHJ1ZSIpXSwgZXhhY3QgPSBGQUxTRSkpJHAudmFsdWUgZWxzZSBOQSwKICAgIHBfdmFsdWVfTElNT05fU1BJRUNFQVNJID0gaWYgKG5fTElNT04gPiAwICYgbl9TUElFQ0VBU0kgPiAwKSAKICAgICAgdGlkeShleGFjdFJhbmtUZXN0czo6d2lsY294LmV4YWN0KENvdW50W0RhdGFUeXBlICVpbiUgYygiTElNT04iLCAiU1BJRUMtRUFTSSIpXSB+IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEYXRhVHlwZVtEYXRhVHlwZSAlaW4lIGMoIkxJTU9OIiwgIlNQSUVDLUVBU0kiKV0sIAogICAgICAgICAgICAgICAgICAgICAgIGV4YWN0ID0gRkFMU0UpKSRwLnZhbHVlIGVsc2UgTkEsCiAgICAgICAgICAgICAgICAgICAgICAgcF92YWx1ZV9TUElFQ0VBU0lfVFJVRSA9IGlmIChuX1NQSUVDRUFTSSA+IDAgJiBuX1RSVUUgPiAwKSAKICAgICAgdGlkeShleGFjdFJhbmtUZXN0czo6d2lsY294LmV4YWN0KENvdW50W0RhdGFUeXBlICVpbiUgYygiU1BJRUMtRUFTSSIsICJUcnVlIildIH4gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERhdGFUeXBlW0RhdGFUeXBlICVpbiUgYygiU1BJRUMtRUFTSSIsICJUcnVlIildLCAKICAgICAgICAgICAgICAgICAgICAgICBleGFjdCA9IEZBTFNFKSkkcC52YWx1ZSBlbHNlIE5BCiAgKSAlPiUKICBtdXRhdGUoCiAgICBzZV9lZGdlc19MSU1PTiA9IHNkX2VkZ2VzX0xJTU9OIC8gc3FydChuX0xJTU9OKSwKICAgIHNlX2VkZ2VzX1NQSUVDRUFTSSA9IHNkX2VkZ2VzX1NQSUVDRUFTSSAvIHNxcnQobl9TUElFQ0VBU0kpLAogICAgc2VfZWRnZXNfVFJVRSA9IHNkX2VkZ2VzX1RSVUUgLyBzcXJ0KG5fVFJVRSksCiAgICBzaWduaWZpY2FuY2VfTElNT05fVFJVRSA9IHNhcHBseShwX3ZhbHVlX0xJTU9OX1RSVUUsIHBfdmFsdWVfc2lnKSwKICAgIHNpZ25pZmljYW5jZV9MSU1PTl9TUElFQ0VBU0kgPSBzYXBwbHkocF92YWx1ZV9MSU1PTl9TUElFQ0VBU0ksIHBfdmFsdWVfc2lnKSwKICAgIHNpZ25pZmljYW5jZV9TUElFQ0VBU0lfVFJVRSA9IHNhcHBseShwX3ZhbHVlX1NQSUVDRUFTSV9UUlVFLCBwX3ZhbHVlX3NpZykKICApCgojIFN0ZXAgMjogTWFrZSB0aGUgcGxvdHRpbmcgZGF0YQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpTdW1tYXJ5X2RhdGFfbG9uZyA8LSBTdW1tYXJ5X2RhdGEgJT4lCiAgZHBseXI6OnNlbGVjdChDb25uZWN0YW5jZSwgc3RhcnRzX3dpdGgoIm1lYW5fZWRnZXMiKSwgc3RhcnRzX3dpdGgoInNlX2VkZ2VzIiksIHN0YXJ0c193aXRoKCJzaWduaWZpY2FuY2UiKSkgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgibWVhbl9lZGdlcyIpLCBuYW1lc190byA9ICJEYXRhVHlwZSIsIHZhbHVlc190byA9ICJtZWFuX2VkZ2VzIikgJT4lCiAgbXV0YXRlKERhdGFUeXBlID0gY2FzZV93aGVuKAogICAgc3RyX2RldGVjdChEYXRhVHlwZSwgIkxJTU9OIikgfiAiTElNT04iLAogICAgc3RyX2RldGVjdChEYXRhVHlwZSwgIlNQSUVDRUFTSSIpIH4gIlNQSUVDLUVBU0kiLAogICAgc3RyX2RldGVjdChEYXRhVHlwZSwgIlRSVUUiKSB+ICJUcnVlIgogICkpICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gc3RhcnRzX3dpdGgoInNlX2VkZ2VzIiksIG5hbWVzX3RvID0gIkRhdGFUeXBlX3NlIiwgdmFsdWVzX3RvID0gInNlX2VkZ2VzIikgJT4lCiAgbXV0YXRlKERhdGFUeXBlX3NlID0gY2FzZV93aGVuKAogICAgc3RyX2RldGVjdChEYXRhVHlwZV9zZSwgIkxJTU9OIikgfiAiTElNT04iLAogICAgc3RyX2RldGVjdChEYXRhVHlwZV9zZSwgIlNQSUVDRUFTSSIpIH4gIlNQSUVDLUVBU0kiLAogICAgc3RyX2RldGVjdChEYXRhVHlwZV9zZSwgIlRSVUUiKSB+ICJUcnVlIgogICkpICU+JQogIGZpbHRlcihEYXRhVHlwZSA9PSBEYXRhVHlwZV9zZSkgJT4lCiAgZHBseXI6OnNlbGVjdCgtRGF0YVR5cGVfc2UpCgojIEFkZCBzaWduaWZpY2FuY2UgbGFiZWxzClN1bW1hcnlfZGF0YV9sb25nIDwtIFN1bW1hcnlfZGF0YV9sb25nICU+JQogIGxlZnRfam9pbigKICAgIFN1bW1hcnlfZGF0YSAlPiUgZHBseXI6OnNlbGVjdChDb25uZWN0YW5jZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lnbmlmaWNhbmNlX0xJTU9OX1RSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpZ25pZmljYW5jZV9MSU1PTl9TUElFQ0VBU0ksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpZ25pZmljYW5jZV9TUElFQ0VBU0lfVFJVRSksCiAgICBieSA9ICJDb25uZWN0YW5jZSIKICApCmBgYAoKCgoKYGBge3J9CiMgU3RlcCAzOiBGaW5hbCBmaWd1cmUKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKZ2dwbG90KFN1bW1hcnlfZGF0YV9sb25nLCBhZXMoeCA9IENvbm5lY3RhbmNlLCB5ID0gbWVhbl9lZGdlcywgY29sb3IgPSBEYXRhVHlwZSkpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludCgpICsKICAKICAjIEFkZCBFcnJvciBiYXJzCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbiA9IG1lYW5fZWRnZXMgLSBzZV9lZGdlcywgCiAgICAgICAgICAgICAgICAgICAgeW1heCA9IG1lYW5fZWRnZXMgKyBzZV9lZGdlcyksIHdpZHRoID0gMC4wMSkgKwogIAogICAgIyBBZGQgYSBsZWdlbmQgdG8gZGVzY3JpYmUgYWxsIHRoZSBjb2xvcnMKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZSA9ICJOZXR3b3JrIiwgCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoIkxJTU9OIiA9ICJvcmFuZ2UiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU1BJRUMtRUFTSSIgPSAiYmx1ZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUcnVlIiA9ICJncmV5IiksCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkxJTU9OIiA9ICJMSU1PTiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTUElFQy1FQVNJIiA9ICJTUElFQy1FQVNJICsgQ292YXJpYXRlcyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUcnVlIiA9ICJTUElFQy1FQVNJIC0gTm8gQ292YXJpYXRlcyIpKSArCiAgCiAgIyBVc2UgZ2duZXdzY2FsZSB0byBkZWZpbmUgYSBuZXcgY29sb3Igc2NhbGUgYWZ0ZXIgdGhlIGZpcnN0IG9uZQogIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9jb2xvcigpICsKICAKICAjIEFubm90YXRlIExJTU9OIHZzIFRSVUUgc2lnbmlmaWNhbmNlIGxldmVsCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHNpZ25pZmljYW5jZV9MSU1PTl9UUlVFLngsIHkgPSAobWVhbl9lZGdlcyArIHNlX2VkZ2VzICsgNSksIAogICAgICAgICAgICAgICAgY29sb3IgPSBmYWN0b3IoIkxJTU9OLVRydWUiKSksIAogICAgICAgICAgICBzaXplID0gNCwgdmp1c3QgPSAtMC41LCBjaGVja19vdmVybGFwID0gVFJVRSwgCiAgICAgICAgICAgIGRhdGEgPSBTdW1tYXJ5X2RhdGFfbG9uZyAlPiUgZmlsdGVyKERhdGFUeXBlID09ICJMSU1PTiIpKSArIAogIAogICMgQW5ub3RhdGUgTElNT04gdnMgU1BJRUMtRUFTSSBzaWduaWZpY2FuY2UgbGV2ZWwKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gc2lnbmlmaWNhbmNlX0xJTU9OX1NQSUVDRUFTSS54LCB5ID0gKG1lYW5fZWRnZXMgKyBzZV9lZGdlcyArIDE1KSwgCiAgICAgICAgICAgICAgICBjb2xvciA9IGZhY3RvcigiTElNT04tU1BJRUNFQVNJIikpLCAKICAgICAgICAgICAgc2l6ZSA9IDQsIHZqdXN0ID0gLTAuNSwgY2hlY2tfb3ZlcmxhcCA9IFRSVUUsIAogICAgICAgICAgICBkYXRhID0gU3VtbWFyeV9kYXRhX2xvbmcgJT4lIGZpbHRlcihEYXRhVHlwZSA9PSAiTElNT04iKSkgKyAKICAKICAjIEFubm90YXRlIFNQSUVDLUVBU0kgdnMgVFJVRSBzaWduaWZpY2FuY2UgbGV2ZWwKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gc2lnbmlmaWNhbmNlX1NQSUVDRUFTSV9UUlVFLngsIHkgPSAobWVhbl9lZGdlcyArIHNlX2VkZ2VzICsgOCksIAogICAgICAgICAgICAgICAgY29sb3IgPSBmYWN0b3IoIlNQSUVDRUFTSS1UcnVlIikpLCAKICAgICAgICAgICAgc2l6ZSA9IDQsIHZqdXN0ID0gLTAuNSwgY2hlY2tfb3ZlcmxhcCA9IFRSVUUsIAogICAgICAgICAgICBkYXRhID0gU3VtbWFyeV9kYXRhX2xvbmcgJT4lIGZpbHRlcihEYXRhVHlwZSA9PSAiU1BJRUMtRUFTSSIpKSArIAogIAogICMgQWRkIG9uIGxhYmVscyBhbmQgdGhlbWVzCiAgbGFicyh4ID0gIkNvbm5lY3RhbmNlIiwgeSA9ICJUb3RhbCBOZXR3b3JrIEVkZ2VzIikgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAuMTAsIDEuMDApLCBicmVha3MgPSBjKDAuMSwwLjIwLDAuNTAsMC43NSwwLjkwKSkgICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJhcmlhbCIsY29sb3IgPSAiYmxhY2siLCBzaXplID0gMTgpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJhcmlhbCIsY29sb3IgPSAiYmxhY2siLCBzaXplID0gMTgpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiYXJpYWwiLGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDE0KSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gImFyaWFsIixjb2xvciA9ICJibGFjayIsIHNpemUgPSAxNCksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwgICAjIEluY3JlYXNlcyBsZWdlbmQgdGV4dCBzaXplCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMikpICsKICAKICAjIEFkZCBhIGxlZ2VuZCB0byBkZXNjcmliZSBhbGwgdGhlIGNvbG9ycwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lID0gIldpbGNveG9uIHNpZ25maWNhbmNlIiwgCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoIkxJTU9OLVRydWUiID0gImRhcmtvcmFuZ2UyIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxJTU9OLVNQSUVDRUFTSSIgPSAiYmxhY2siLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTUElFQ0VBU0ktVHJ1ZSIgPSAiZGVlcHNreWJsdWUiKSwKICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiTElNT04tVHJ1ZSIgPSAiTElNT04qTm8gQ292IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxJTU9OLVNQSUVDRUFTSSIgPSAiTElNT04qU1BJRUNFQVNJIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU1BJRUNFQVNJLVRydWUiID0gIlNQSUVDRUFTSSpObyBDb3YiKSkKYGBgCgoKCgojIyAzLjMgIFN0cmVuZ3RoIG9mIFJlY292ZXJlZCBFZGdlcwoKCiMjIyAzLjMuMSAtIERhdGEgRm9ybWF0dGluZwpSZWFkIGJhY2sgaW4gdGhlIHJhdyBkYXRhIHdpdGggbm8gY292YXJpYXRlcywgZ2V0IHRoZSBzcGVhcm1hbiBjb3JyZWxhdGlvbiBwZXIgdGltZSBwb2ludCAoMSwyLDMsNCw1KSBhbmQgcGVyIHNhbXBsZSBzaXplIGxldmVsICgxMCwyMCw1MCw3NSwxMDApLiBXZSB3aWxsIHRoZW4gY29tcGFyZSB0aGVzZSBzdHJlbmd0aCBvZiByZWNvdmVyZWQgZWRnZXMgdG8gdGhlIGVkZ2VzIHJlY292ZXJlZCBieSBvdXIgdmFyaW91cyB0ZXN0cyBhYm92ZS4gCmBgYHtyfQpSYXcxMCA8LSByZWFkLmNzdihoZXJlKCJEYXRhIiwgIkdMVl9TaW1EYXRhIiwgIkRhdGFzZXRfNCIsIkdMVl8wLjEwLmNzdiIpKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl90b19yb3duYW1lcygiWCIpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdCgtYyhUaW1lLCBJRCwgU2V4LCBCTUksIEFnZSkpClJhdzIwIDwtIHJlYWQuY3N2KGhlcmUoIkRhdGEiLCAiR0xWX1NpbURhdGEiLCAiRGF0YXNldF80IiwiR0xWXzAuMjAuY3N2IikpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX3RvX3Jvd25hbWVzKCJYIikgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6c2VsZWN0KC1jKFRpbWUsIElELCBTZXgsIEJNSSwgQWdlKSkKUmF3NTAgPC0gcmVhZC5jc3YoaGVyZSgiRGF0YSIsICJHTFZfU2ltRGF0YSIsICJEYXRhc2V0XzQiLCJHTFZfMC41MC5jc3YiKSkgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fdG9fcm93bmFtZXMoIlgiKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRwbHlyOjpzZWxlY3QoLWMoVGltZSwgSUQsIFNleCwgQk1JLCBBZ2UpKQpSYXc3NSA8LSByZWFkLmNzdihoZXJlKCJEYXRhIiwgIkdMVl9TaW1EYXRhIiwgIkRhdGFzZXRfNCIsIkdMVl8wLjc1LmNzdiIpKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl90b19yb3duYW1lcygiWCIpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdCgtYyhUaW1lLCBJRCwgU2V4LCBCTUksIEFnZSkpClJhdzEwMCA8LSByZWFkLmNzdihoZXJlKCJEYXRhIiwgIkdMVl9TaW1EYXRhIiwgIkRhdGFzZXRfNCIsIkdMVl8wLjkwLmNzdiIpKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl90b19yb3duYW1lcygiWCIpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdCgtYyhUaW1lLCBJRCwgU2V4LCBCTUksIEFnZSkpCmBgYAoKCgpgYGB7cn0KIyBEZWZpbmUgc2FtcGxlIHNpemVzIGFuZCBjb3JyZXNwb25kaW5nIGRhdGEgZnJhbWVzCmNvbm5lY3RhbmNlX3NpemUgPC0gYygwLjEwLCAwLjIwLCAwLjUwLCAwLjc1LCAwLjkwKQpkYXRhX2ZyYW1lcyA8LSBsaXN0KFJhdzEwLCBSYXcyMCwgUmF3NTAsIFJhdzc1LCBSYXcxMDApCgojIEluaXRpYWxpemUgYW4gZW1wdHkgbGlzdCB0byBzdG9yZSByZXN1bHRzIGZvciBlYWNoIHNhbXBsZSBzaXplCmNvdl9yZXN1bHRzIDwtIGxpc3QoKQoKZm9yIChpIGluIHNlcV9hbG9uZyhjb25uZWN0YW5jZV9zaXplKSkgewogIGRmIDwtIGRhdGFfZnJhbWVzW1tpXV0KICBzYW1wbGVfc2l6ZSA8LSBjb25uZWN0YW5jZV9zaXplW2ldCiAgCiAgZGYkU3ViamVjdCA8LSBzdWIoIl9UaW1lWzAtOV0rIiwgIiIsIHJvd25hbWVzKGRmKSkKICBkZiRUaW1lcG9pbnQgPC0gc3ViKCJTYmpbMC05XStfIiwgIiIsIHJvd25hbWVzKGRmKSkKICBkZiRUaW1lcG9pbnQgPC0gZ3N1YigiVGltZSIsICIiLCBkZiRUaW1lcG9pbnQpCiAgCiAgIyBMaXN0IG9mIHVuaXF1ZSB0aW1lcG9pbnRzCiAgdGltZXBvaW50cyA8LSB1bmlxdWUoZGYkVGltZXBvaW50KQogIAogICMgSW5pdGlhbGl6ZSBhbiBlbXB0eSBkYXRhIGZyYW1lIHRvIHN0b3JlIHJlc3VsdHMKICByZXN1bHRfZGYgPC0gZGF0YS5mcmFtZShTb3VyY2UgPSBjaGFyYWN0ZXIoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBTaW5rID0gY2hhcmFjdGVyKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgQ292YXJpYW5jZSA9IG51bWVyaWMoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBUaW1lcG9pbnQgPSBjaGFyYWN0ZXIoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCiAgCiAgIyBMb29wIHRocm91Z2ggZWFjaCB0aW1lcG9pbnQKICBmb3IgKHRpbWUgaW4gdGltZXBvaW50cykgewogICAgIyBTdWJzZXQgZGF0YSBmb3IgdGhlIGN1cnJlbnQgdGltZXBvaW50CiAgICBkZl90aW1lIDwtIGRmICU+JSBmaWx0ZXIoVGltZXBvaW50ID09IHRpbWUpICU+JSBkcGx5cjo6c2VsZWN0KC1TdWJqZWN0LCAtVGltZXBvaW50KQogICAgCiAgICAjIENhbGN1bGF0ZSBjb3ZhcmlhbmNlIG1hdHJpeCBmb3IgdGhlIGN1cnJlbnQgdGltZXBvaW50CiAgICBjb3ZfbWF0cml4IDwtIGNvcihkZl90aW1lLCBtZXRob2QgPSAic3BlYXJtYW4iKQogICAgCiAgICAjIENvbnZlcnQgdGhlIGNvdmFyaWFuY2UgbWF0cml4IHRvIGEgbG9uZyBmb3JtYXQKICAgIGNvdl9sb25nIDwtIGFzLmRhdGEuZnJhbWUoYXMudGFibGUoY292X21hdHJpeCkpCiAgICAKICAgICMgUmVuYW1lIGNvbHVtbnMgZm9yIGNsYXJpdHkKICAgIG5hbWVzKGNvdl9sb25nKSA8LSBjKCJTaW5rIiwgIlNvdXJjZSIsICAiQ292YXJpYW5jZSIpCiAgICAKICAgICMgQWRkIHRpbWVwb2ludCBpbmZvcm1hdGlvbgogICAgY292X2xvbmckVGltZXBvaW50IDwtIHRpbWUKICAgIAogICAgIyBDb21iaW5lIHdpdGggdGhlIHJlc3VsdCBkYXRhIGZyYW1lCiAgICByZXN1bHRfZGYgPC0gYmluZF9yb3dzKHJlc3VsdF9kZiwgY292X2xvbmcpCiAgfQogIAogICMgQWRkIHNhbXBsZSBzaXplIGluZm9ybWF0aW9uCiAgcmVzdWx0X2RmIDwtIHJlc3VsdF9kZiAlPiUgZHBseXI6OnJlbmFtZSgiVGltZSIgPSJUaW1lcG9pbnQiKQogIHJlc3VsdF9kZiRDb25uZWN0YW5jZSA8LSBzYW1wbGVfc2l6ZQogIAogICMgU3RvcmUgaW4gdGhlIGxpc3QKICBjb3ZfcmVzdWx0c1tbaV1dIDwtIHJlc3VsdF9kZgp9CgojIE1lcmdlIGFsbCByZXN1bHRzIGludG8gb25lIGRhdGEgZnJhbWUKVHJ1ZV9jb3YgPC0gZG8uY2FsbChyYmluZCwgY292X3Jlc3VsdHMpCmBgYAoKCk1lcmdlIHRvZ2V0aGVyIGFuZCBhZGQgYW5ub3RhaXRvbiBmb3Igc3RyZW5ndGggb2YgZWRnZXMgZGV0ZWN0ZWQgIAotIHRoZSB4IGF4aXMgd2lsbCBiZSBhIHJhbmdlIGZyb20gdGhlICJDb3ZhcmlhbmNlIiBjb2x1bW4gd2l0aCBlZGdlcyBiZXR3ZWVuICAtMSAtIC0wLjUsIC0wLjUgLSAtMC4xLCAtMC4xIC0gMC4xLCAwLjEgLTAuNSwgMC41IC0gMS4gIAotIHRoZSB5IGF4aXMgd2lsbCBiZSB3aGF0IHBlcmNlbnRhZ2Ugb2YgdGhlIGVkZ2VzIGRldGVjdGVkIGZvciB0aGF0IG5ldHdvcmsvbWV0aG9kIGZhbGwgaW50byBlYWNoIG9mIHRoZXNlIGNhdGVnb3JpZXMKYGBge3J9CiMgTWVyZ2Ugd2l0aCBteSBkYXRhClN0cmVuZ3RoX2RhdGEgPC0gbWVyZ2UoVHJ1ZV9jb3YsIFN1YmplY3Rfc2Vuc19kYXRhX3RheGEsIGJ5LnkgPSBjKCJTb3VyY2UiLCAiU2luayIsICJUaW1lIiwgIkNvbm5lY3RhbmNlIiksIGFsbCA9IFRSVUUpCgojIEFkZCBpbiBjb2x1bW5zIGJhc2VkIG9uIHdoYXQgc3RyZW5ndGggb2YgYXNzb2NpYXRpb24gaXQgZGV0ZWN0ZWQKU3RyZW5ndGhfZGF0YSA8LSBTdHJlbmd0aF9kYXRhICU+JSAKICAjIGZvciBMSU1PTiByZWNvdmVyeQogIG11dGF0ZShMSU1PTl9zdHJlbmd0aCA9IGNhc2Vfd2hlbigKICAgICFpcy5uYShMX0VkZ2Vfd2VpZ2h0KSAmIENvdmFyaWFuY2UgPj0gLTEgJiBDb3ZhcmlhbmNlIDwgLTAuNSB+ICItMSAtIC0wLjUiLAogICAgIWlzLm5hKExfRWRnZV93ZWlnaHQpICYgQ292YXJpYW5jZSA+PSAtMC41ICYgQ292YXJpYW5jZSA8IC0wLjEgfiAiLTAuNSAtIC0wLjEiLAogICAgIWlzLm5hKExfRWRnZV93ZWlnaHQpICYgQ292YXJpYW5jZSA+PSAtMC4xICYgQ292YXJpYW5jZSA8IDAuMSB+ICItMC4xIC0gMC4xIiwKICAgICFpcy5uYShMX0VkZ2Vfd2VpZ2h0KSAmIENvdmFyaWFuY2UgPj0gMC4xICYgQ292YXJpYW5jZSA8IDAuNSB+ICIwLjEgLSAwLjUiLAogICAgIWlzLm5hKExfRWRnZV93ZWlnaHQpICYgQ292YXJpYW5jZSA+PSAwLjUgJiBDb3ZhcmlhbmNlIDw9IDEgfiAiMC41IC0gMSIsCiAgICBUUlVFIH4gYXMuY2hhcmFjdGVyKE5BKSAgCiAgKSkgJT4lIAogICMgZm9yIFNQSUVDLUVBU0kgcmVjb3ZlcnkKICBtdXRhdGUoU1BJRUNFQVNJX3N0cmVuZ3RoID0gY2FzZV93aGVuKAogICAgIWlzLm5hKENvdl9FZGdlX3dlaWdodCkgJiBDb3ZhcmlhbmNlID49IC0xICYgQ292YXJpYW5jZSA8IC0wLjUgfiAiLTEgLSAtMC41IiwKICAgICFpcy5uYShDb3ZfRWRnZV93ZWlnaHQpICYgQ292YXJpYW5jZSA+PSAtMC41ICYgQ292YXJpYW5jZSA8IC0wLjEgfiAiLTAuNSAtIC0wLjEiLAogICAgIWlzLm5hKENvdl9FZGdlX3dlaWdodCkgJiBDb3ZhcmlhbmNlID49IC0wLjEgJiBDb3ZhcmlhbmNlIDwgMC4xIH4gIi0wLjEgLSAwLjEiLAogICAgIWlzLm5hKENvdl9FZGdlX3dlaWdodCkgJiBDb3ZhcmlhbmNlID49IDAuMSAmIENvdmFyaWFuY2UgPCAwLjUgfiAiMC4xIC0gMC41IiwKICAgICFpcy5uYShDb3ZfRWRnZV93ZWlnaHQpICYgQ292YXJpYW5jZSA+PSAwLjUgJiBDb3ZhcmlhbmNlIDw9IDEgfiAiMC41IC0gMSIsCiAgICBUUlVFIH4gYXMuY2hhcmFjdGVyKE5BKSAgCiAgKSkKCiMgU3BsaXQgaW50byBUcnVlIFBvc2l0aXZlcyBEYXRhc2V0IGFuZCBGYWxzZSBQb3NpdGl2ZXMgRGF0YXNldHMKVHJ1ZV9wb3NfcmF3IDwtIFN0cmVuZ3RoX2RhdGEgJT4lIGZpbHRlcighaXMubmEoT19FZGdlX3dlaWdodCkpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKCEoaXMubmEoTF9FZGdlX3dlaWdodCkgJiBpcy5uYShDb3ZfRWRnZV93ZWlnaHQpKSkKRmFsc2VfcG9zX3JhdyA8LSBTdHJlbmd0aF9kYXRhICU+JSBmaWx0ZXIoaXMubmEoT19FZGdlX3dlaWdodCkpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKCEoaXMubmEoTF9FZGdlX3dlaWdodCkgJiBpcy5uYShDb3ZfRWRnZV93ZWlnaHQpKSkKCmBgYAoKCiMjIyAzLjMuMiAtIFN0YXRpc3RpY3MKCgpUcnVlIFBvc2l0aXZlcwpgYGB7cn0KIyBTdGVwIDEgdHVybiB0byBsb25nIGZvcm1hdAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKbG9uZ19kYXRhIDwtIFRydWVfcG9zX3JhdyAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IGMoIkxJTU9OX3N0cmVuZ3RoIiwgIlNQSUVDRUFTSV9zdHJlbmd0aCIpLAogICAgICAgICAgICAgICBuYW1lc190byA9ICJOZXR3b3JrIiwKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIldlaWdodCIsCiAgICAgICAgICAgICAgIHZhbHVlc19kcm9wX25hID0gVFJVRSkgJT4lCiAgbXV0YXRlKE5ldHdvcmsgPSBjYXNlX3doZW4oCiAgICBOZXR3b3JrID09ICJMSU1PTl9zdHJlbmd0aCIgfiAiTElNT04iLCAKICAgIE5ldHdvcmsgPT0gIlNQSUVDRUFTSV9zdHJlbmd0aCIgfiAiU1BJRUNFQVNJIikpIAoKCiMgU3RlcCAyOiBGaW5kIHRvdGFsIG5ldHdvcmtzIHBlciBOZXR3b3JrIHR5cGUgYW5kIHNhbXBsZSBzaXplCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1bW1hcnlfZGF0YSA8LSBsb25nX2RhdGEgJT4lCiAgZ3JvdXBfYnkoTmV0d29yaywgQ29ubmVjdGFuY2UpICU+JQogIG11dGF0ZShUb3RhbF9lZGdlID0gc3VtKCFpcy5uYShXZWlnaHQpKSkKCiMgU3RlcCAzOiBUdXJuIHRvIHBlcmNlbnRhZ2VzIGF2ZXJhZ2luZyBieSB0aW1lIHRvIHBsb3QKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKc3VtbWFyeV9kYXRhMiA8LSBzdW1tYXJ5X2RhdGEgJT4lCiAgZ3JvdXBfYnkoTmV0d29yaywgQ29ubmVjdGFuY2UpICU+JQogIG11dGF0ZSh0b3RhbF9jb3VudCA9IG4oKSkgJT4lCiAgZ3JvdXBfYnkoTmV0d29yaywgQ29ubmVjdGFuY2UsIFdlaWdodCkgJT4lCiAgc3VtbWFyaXplKGNvdW50ID0gbigpLAogICAgICAgICAgICBtZWFuX3BlcmNlbnRhZ2UgPSAoY291bnQgLyBkcGx5cjo6Zmlyc3QodG90YWxfY291bnQpKSwgCiAgICAgICAgICAgIC5ncm91cHMgPSAnZHJvcCcpCgoKIyBTdGVwIDQ6IFBsb3QKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTdGFja2VkICsgcGVyY2VudApnZ3Bsb3Qoc3VtbWFyeV9kYXRhMiwgYWVzKGZpbGwgPSBXZWlnaHQsIHkgPSBtZWFuX3BlcmNlbnRhZ2UsIHggPSAgZmFjdG9yKENvbm5lY3RhbmNlKSkpICsKICBnZW9tX2Jhcihwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC44KSwgc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoID0gMC43KSArIAogIGZhY2V0X3dyYXAofiBOZXR3b3JrKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWUgPSAiRWRnZSBXZWlnaHQiLAogICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoIi0xIC0gLTAuNSIgPSAiZGFya3JlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiLTAuNSAtIC0wLjEiID0gImluZGlhbnJlZDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIi0wLjEgLSAwLjEiID0gIm1vY2Nhc2luIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIwLjEgLSAwLjUiID0gImxpZ2h0Ymx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMC41IC0gMSIgPSAiZGFya2JsdWUiKSwKICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKCItMSAtIC0wLjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIi0wLjUgLSAtMC4xIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICItMC4xIC0gMC4xIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIwLjEgLSAwLjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjAuNSAtIDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5BIikpICsKICAjIEFkZCBvbiBsYWJlbHMgYW5kIHRoZW1lcwogIGxhYnMoeCA9ICJDb25uZWN0YW5jZSIsIHkgPSAiUGVyY2VudCBUcnVlIFBvc2l0aXZlcyIpICsKICB5bGltKDAsMSkgKwogIHNjYWxlX3hfZGlzY3JldGUoKSArCiAgdGhlbWVfbGluZWRyYXcoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gImFyaWFsIixjb2xvciA9ICJibGFjayIsIHNpemUgPSAxNCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gImFyaWFsIixjb2xvciA9ICJibGFjayIsIHNpemUgPSAxNCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJhcmlhbCIsY29sb3IgPSAiYmxhY2siLCBzaXplID0gMTQpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiYXJpYWwiLGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDE0KSwKICAgICAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIGZhbWlseSA9ICJhcmlhbCIsIGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDEyKSkKYGBgCgoKCkZhbHNlIFBvc2l0aXZlcwpgYGB7cn0KIyBTdGVwIDEgdHVybiB0byBsb25nIGZvcm1hdAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKbG9uZ19kYXRhIDwtIEZhbHNlX3Bvc19yYXcgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKCJMSU1PTl9zdHJlbmd0aCIsICJTUElFQ0VBU0lfc3RyZW5ndGgiKSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiTmV0d29yayIsCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJXZWlnaHQiLAogICAgICAgICAgICAgICB2YWx1ZXNfZHJvcF9uYSA9IFRSVUUpICU+JQogIG11dGF0ZShOZXR3b3JrID0gY2FzZV93aGVuKAogICAgTmV0d29yayA9PSAiTElNT05fc3RyZW5ndGgiIH4gIkxJTU9OIiwgCiAgICBOZXR3b3JrID09ICJTUElFQ0VBU0lfc3RyZW5ndGgiIH4gIlNQSUVDRUFTSSIpKSAKCgojIFN0ZXAgMjogRmluZCB0b3RhbCBuZXR3b3JrcyBwZXIgTmV0d29yayB0eXBlIGFuZCBzYW1wbGUgc2l6ZQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdW1tYXJ5X2RhdGEgPC0gbG9uZ19kYXRhICU+JQogIGdyb3VwX2J5KE5ldHdvcmssIENvbm5lY3RhbmNlKSAlPiUKICBtdXRhdGUoVG90YWxfZWRnZSA9IHN1bSghaXMubmEoV2VpZ2h0KSkpCgojIFN0ZXAgMzogVHVybiB0byBwZXJjZW50YWdlcyBhdmVyYWdpbmcgYnkgdGltZSB0byBwbG90CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1bW1hcnlfZGF0YTIgPC0gc3VtbWFyeV9kYXRhICU+JQogIGdyb3VwX2J5KE5ldHdvcmssIENvbm5lY3RhbmNlKSAlPiUKICBtdXRhdGUodG90YWxfY291bnQgPSBuKCkpICU+JQogIGdyb3VwX2J5KE5ldHdvcmssIENvbm5lY3RhbmNlLCBXZWlnaHQpICU+JQogIHN1bW1hcml6ZShjb3VudCA9IG4oKSwKICAgICAgICAgICAgbWVhbl9wZXJjZW50YWdlID0gKGNvdW50IC8gZHBseXI6OmZpcnN0KHRvdGFsX2NvdW50KSksIAogICAgICAgICAgICAuZ3JvdXBzID0gJ2Ryb3AnKQoKCiMgU3RlcCA0OiBQbG90CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU3RhY2tlZCArIHBlcmNlbnQKZ2dwbG90KHN1bW1hcnlfZGF0YTIsIGFlcyhmaWxsPVdlaWdodCwgeT1tZWFuX3BlcmNlbnRhZ2UsIHg9YXMuZmFjdG9yKENvbm5lY3RhbmNlKSkpICsgCiAgZ2VvbV9iYXIocG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuOCksIHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDAuNykgKyAKICBmYWNldF93cmFwKH4gTmV0d29yaykgKyAKICBzY2FsZV9maWxsX21hbnVhbChuYW1lID0gIkVkZ2UgV2VpZ2h0IiwKICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCItMSAtIC0wLjUiID0gImRhcmtyZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIi0wLjUgLSAtMC4xIiA9ICJpbmRpYW5yZWQxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICItMC4xIC0gMC4xIiA9ICJtb2NjYXNpbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMC4xIC0gMC41IiA9ICJsaWdodGJsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjAuNSAtIDEiID0gImRhcmtibHVlIiksCiAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygiLTEgLSAtMC41IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICItMC41IC0gLTAuMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiLTAuMSAtIDAuMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMC4xIC0gMC41IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIwLjUgLSAxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOQSIpKSArCiAgIyBBZGQgb24gbGFiZWxzIGFuZCB0aGVtZXMKICBsYWJzKHggPSAiQ29ubmVjdGFuY2UiLCB5ID0gIlBlcmNlbnQgRmFsc2UgUG9zaXRpdmVzIikgKwogIHlsaW0oMCwxKSArCiAgc2NhbGVfeF9kaXNjcmV0ZSgpICsKICB0aGVtZV9saW5lZHJhdygpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiYXJpYWwiLGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDE0KSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiYXJpYWwiLGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDE0KSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gImFyaWFsIixjb2xvciA9ICJibGFjayIsIHNpemUgPSAxNCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJhcmlhbCIsY29sb3IgPSAiYmxhY2siLCBzaXplID0gMTQpLAogICAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgZmFtaWx5ID0gImFyaWFsIiwgY29sb3IgPSAid2hpdGUiLCBzaXplID0gMTIpKQpgYGAKCgoKCiMgNCAtIE5vdGlmaWNhdGlvbnMKYGBge3J9CiNzeXN0ZW0oInNheSBZb3VyIFNpbGx5IGNvZGUgZmluaXNoZWQhIikKYGBgCgoKCgoKCgoKCgoKCgoKCgoKCgo=